blob: ecb9c2e5fad696a0948b83e396750c0501302874 [file] [log] [blame]
// 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 "meta-service/meta_service.h"
#include <brpc/controller.h>
#include <bvar/window.h>
#include <fmt/core.h>
#include <gen_cpp/cloud.pb.h>
#include <gen_cpp/olap_file.pb.h>
#include <google/protobuf/repeated_field.h>
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <memory>
#include <random>
#include <string>
#include <thread>
#include "common/config.h"
#include "common/logging.h"
#include "common/util.h"
#include "cpp/sync_point.h"
#include "meta-service/meta_service_helper.h"
#include "meta-store/blob_message.h"
#include "meta-store/document_message.h"
#include "meta-store/keys.h"
#include "meta-store/mem_txn_kv.h"
#include "meta-store/txn_kv.h"
#include "meta-store/txn_kv_error.h"
#include "meta-store/versioned_value.h"
#include "mock_resource_manager.h"
#include "rate-limiter/rate_limiter.h"
#include "resource-manager/resource_manager.h"
int main(int argc, char** argv) {
const std::string conf_file = "doris_cloud.conf";
if (!doris::cloud::config::init(conf_file.c_str(), true)) {
std::cerr << "failed to init config file, conf=" << conf_file << std::endl;
return -1;
}
config::enable_retry_txn_conflict = false;
config::enable_txn_store_retry = true;
config::txn_store_retry_base_intervals_ms = 1;
config::txn_store_retry_times = 20;
config::enable_check_instance_id = false;
if (!doris::cloud::init_glog("meta_service_test")) {
std::cerr << "failed to init glog" << std::endl;
return -1;
}
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
namespace doris::cloud {
std::unique_ptr<MetaServiceProxy> get_meta_service(bool mock_resource_mgr) {
int ret = 0;
// MemKv
auto txn_kv = std::dynamic_pointer_cast<TxnKv>(std::make_shared<MemTxnKv>());
if (txn_kv != nullptr) {
ret = txn_kv->init();
[&] { ASSERT_EQ(ret, 0); }();
}
[&] { ASSERT_NE(txn_kv.get(), nullptr); }();
// FdbKv
// config::fdb_cluster_file_path = "fdb.cluster";
// static auto txn_kv = std::dynamic_pointer_cast<TxnKv>(std::make_shared<FdbTxnKv>());
// static std::atomic<bool> init {false};
// bool tmp = false;
// if (init.compare_exchange_strong(tmp, true)) {
// int ret = txn_kv->init();
// [&] { ASSERT_EQ(ret, 0); ASSERT_NE(txn_kv.get(), nullptr); }();
// }
std::unique_ptr<Transaction> txn;
EXPECT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->remove("\x00", "\xfe"); // This is dangerous if the fdb is not correctly set
EXPECT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
auto rs = mock_resource_mgr ? std::make_shared<MockResourceManager>(txn_kv)
: std::make_shared<ResourceManager>(txn_kv);
auto rl = std::make_shared<RateLimiter>();
auto snapshot = std::make_shared<SnapshotManager>(txn_kv);
auto meta_service = std::make_unique<MetaServiceImpl>(txn_kv, rs, rl, snapshot);
return std::make_unique<MetaServiceProxy>(std::move(meta_service));
}
std::unique_ptr<MetaServiceProxy> get_meta_service() {
return get_meta_service(true);
}
std::unique_ptr<MetaServiceProxy> get_fdb_meta_service() {
config::fdb_cluster_file_path = "fdb.cluster";
static auto txn_kv = std::dynamic_pointer_cast<TxnKv>(std::make_shared<FdbTxnKv>());
static std::atomic<bool> init {false};
bool tmp = false;
if (init.compare_exchange_strong(tmp, true)) {
int ret = txn_kv->init();
[&] {
ASSERT_EQ(ret, 0);
ASSERT_NE(txn_kv.get(), nullptr);
}();
}
auto rs = std::make_shared<MockResourceManager>(txn_kv);
auto rl = std::make_shared<RateLimiter>();
auto snapshot = std::make_shared<SnapshotManager>(txn_kv);
auto meta_service = std::make_unique<MetaServiceImpl>(txn_kv, rs, rl, snapshot);
return std::make_unique<MetaServiceProxy>(std::move(meta_service));
}
static std::string next_rowset_id() {
static int cnt = 0;
return std::to_string(++cnt);
}
void add_tablet(CreateTabletsRequest& req, int64_t table_id, int64_t index_id, int64_t partition_id,
int64_t tablet_id) {
auto tablet = req.add_tablet_metas();
tablet->set_table_id(table_id);
tablet->set_index_id(index_id);
tablet->set_partition_id(partition_id);
tablet->set_tablet_id(tablet_id);
auto schema = tablet->mutable_schema();
schema->set_schema_version(0);
auto first_rowset = tablet->add_rs_metas();
first_rowset->set_rowset_id(0); // required
first_rowset->set_rowset_id_v2(next_rowset_id());
first_rowset->set_start_version(0);
first_rowset->set_end_version(1);
first_rowset->mutable_tablet_schema()->CopyFrom(*schema);
}
void create_tablet(MetaServiceProxy* meta_service, int64_t table_id, int64_t index_id,
int64_t partition_id, int64_t tablet_id) {
brpc::Controller cntl;
CreateTabletsRequest req;
CreateTabletsResponse res;
req.set_db_id(1); // default db_id
add_tablet(req, table_id, index_id, partition_id, tablet_id);
meta_service->create_tablets(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << tablet_id;
}
static void create_tablet_with_db_id(MetaServiceProxy* meta_service, int64_t db_id,
int64_t table_id, int64_t index_id, int64_t partition_id,
int64_t tablet_id) {
brpc::Controller cntl;
CreateTabletsRequest req;
CreateTabletsResponse res;
req.set_db_id(db_id);
add_tablet(req, table_id, index_id, partition_id, tablet_id);
meta_service->create_tablets(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << tablet_id;
}
static void begin_txn(MetaServiceProxy* meta_service, int64_t db_id, const std::string& label,
int64_t table_id, int64_t& txn_id) {
brpc::Controller cntl;
BeginTxnRequest req;
BeginTxnResponse res;
auto txn_info = req.mutable_txn_info();
txn_info->set_db_id(db_id);
txn_info->set_label(label);
txn_info->add_table_ids(table_id);
txn_info->set_timeout_ms(36000);
meta_service->begin_txn(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
ASSERT_TRUE(res.has_txn_id()) << label;
txn_id = res.txn_id();
}
static void commit_txn(MetaServiceProxy* meta_service, int64_t db_id, int64_t txn_id,
const std::string& label) {
brpc::Controller cntl;
CommitTxnRequest req;
CommitTxnResponse res;
req.set_db_id(db_id);
req.set_txn_id(txn_id);
meta_service->commit_txn(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK)
<< label << ", res=" << res.ShortDebugString();
}
doris::RowsetMetaCloudPB create_rowset(int64_t txn_id, int64_t tablet_id, int partition_id = 10,
int64_t version = -1, int num_rows = 100) {
doris::RowsetMetaCloudPB rowset;
rowset.set_rowset_id(0); // required
rowset.set_rowset_id_v2(next_rowset_id());
rowset.set_tablet_id(tablet_id);
rowset.set_partition_id(partition_id);
rowset.set_txn_id(txn_id);
if (version > 0) {
rowset.set_start_version(version);
rowset.set_end_version(version);
}
rowset.set_num_segments(1);
rowset.set_num_rows(num_rows);
rowset.set_data_disk_size(num_rows * 100);
rowset.set_index_disk_size(num_rows * 10);
rowset.set_total_disk_size(num_rows * 110);
rowset.mutable_tablet_schema()->set_schema_version(0);
rowset.set_txn_expiration(::time(nullptr)); // Required by DCHECK
return rowset;
}
static void prepare_rowset(MetaServiceProxy* meta_service, const doris::RowsetMetaCloudPB& rowset,
CreateRowsetResponse& res) {
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->prepare_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
}
void commit_rowset(MetaServiceProxy* meta_service, const doris::RowsetMetaCloudPB& rowset,
CreateRowsetResponse& res) {
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->commit_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
}
static void update_tmp_rowset(MetaServiceProxy* meta_service,
const doris::RowsetMetaCloudPB& rowset, CreateRowsetResponse& res) {
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->update_tmp_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
}
static void get_delete_bitmap_update_lock(MetaServiceProxy* meta_service,
GetDeleteBitmapUpdateLockResponse& res, int64_t db_id,
int64_t table_id, int64_t index_id,
const std::vector<std::array<int64_t, 2>>& tablet_idxes,
int64_t expiration, int64_t lock_id, int64_t initiator,
bool require_compaction_stats) {
brpc::Controller cntl;
GetDeleteBitmapUpdateLockRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_table_id(table_id);
for (const auto& [partition_id, _] : tablet_idxes) {
req.add_partition_ids(partition_id);
}
req.set_expiration(expiration);
req.set_lock_id(lock_id);
req.set_initiator(initiator);
req.set_require_compaction_stats(require_compaction_stats);
for (const auto& [partition_id, tablet_id] : tablet_idxes) {
TabletIndexPB* idx = req.add_tablet_indexes();
idx->set_db_id(db_id);
idx->set_table_id(table_id);
idx->set_index_id(index_id);
idx->set_partition_id(partition_id);
idx->set_tablet_id(tablet_id);
}
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
}
void insert_rowset(MetaServiceProxy* meta_service, int64_t db_id, const std::string& label,
int64_t table_id, int64_t partition_id, int64_t tablet_id) {
int64_t txn_id = 0;
ASSERT_NO_FATAL_FAILURE(begin_txn(meta_service, db_id, label, table_id, txn_id));
CreateRowsetResponse res;
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
prepare_rowset(meta_service, rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
res.Clear();
ASSERT_NO_FATAL_FAILURE(commit_rowset(meta_service, rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
commit_txn(meta_service, db_id, txn_id, label);
}
void insert_rowsets(MetaServiceProxy* meta_service, int64_t db_id, const std::string& label,
int64_t table_id, int64_t partition_id, std::vector<int64_t> tablet_ids) {
int64_t txn_id = 0;
ASSERT_NO_FATAL_FAILURE(begin_txn(meta_service, db_id, label, table_id, txn_id));
for (auto tablet_id : tablet_ids) {
CreateRowsetResponse res;
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
prepare_rowset(meta_service, rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
res.Clear();
ASSERT_NO_FATAL_FAILURE(commit_rowset(meta_service, rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
}
commit_txn(meta_service, db_id, txn_id, label);
}
static void add_tablet_metas(MetaServiceProxy* meta_service, std::string instance_id,
int64_t table_id, int64_t index_id,
const std::vector<std::array<int64_t, 2>>& tablet_idxes,
bool skip_tablet_meta = false) {
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
for (const auto& idx : tablet_idxes) {
int64_t partition_id = idx[0];
int64_t tablet_id = idx[1];
std::string stats_key =
stats_tablet_key({instance_id, table_id, index_id, partition_id, tablet_id});
TabletStatsPB stats;
stats.set_base_compaction_cnt(10);
stats.set_cumulative_compaction_cnt(20);
stats.set_cumulative_point(30);
txn->put(stats_key, stats.SerializeAsString());
if (skip_tablet_meta) {
continue;
}
doris::TabletMetaCloudPB tablet_pb;
tablet_pb.set_table_id(table_id);
tablet_pb.set_index_id(index_id);
tablet_pb.set_partition_id(partition_id);
tablet_pb.set_tablet_id(tablet_id);
tablet_pb.set_tablet_state(doris::TabletStatePB::PB_RUNNING);
auto tablet_meta_key =
meta_tablet_key({instance_id, table_id, index_id, partition_id, tablet_id});
auto tablet_meta_val = tablet_pb.SerializeAsString();
txn->put(tablet_meta_key, tablet_meta_val);
}
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
}
static void start_compaction_job(MetaService* meta_service, int64_t tablet_id,
const std::string& job_id, const std::string& initiator,
int base_compaction_cnt, int cumu_compaction_cnt,
TabletCompactionJobPB::CompactionType type,
StartTabletJobResponse& res,
std::pair<int64_t, int64_t> input_version = {0, 0}) {
brpc::Controller cntl;
StartTabletJobRequest req;
req.mutable_job()->mutable_idx()->set_tablet_id(tablet_id);
auto compaction = req.mutable_job()->add_compaction();
compaction->set_id(job_id);
compaction->set_initiator(initiator);
compaction->set_base_compaction_cnt(base_compaction_cnt);
compaction->set_cumulative_compaction_cnt(cumu_compaction_cnt);
compaction->set_type(type);
long now = time(nullptr);
compaction->set_expiration(now + 12);
compaction->set_lease(now + 3);
if (input_version.second > 0) {
compaction->add_input_versions(input_version.first);
compaction->add_input_versions(input_version.second);
compaction->set_check_input_versions_range(true);
}
meta_service->start_tablet_job(&cntl, &req, &res, nullptr);
};
TEST(MetaServiceTest, GetInstanceIdTest) {
extern std::string get_instance_id(const std::shared_ptr<ResourceManager>& rc_mgr,
const std::string& cloud_unique_id);
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id_err", [&](auto&& args) {
std::string* err = try_any_cast<std::string*>(args[0]);
*err = "can't find node from cache";
});
sp->enable_processing();
auto instance_id =
get_instance_id(meta_service->resource_mgr(), "1:ALBJLH4Q:m-n3qdpyal27rh8iprxx");
ASSERT_EQ(instance_id, "ALBJLH4Q");
// version not support
instance_id = get_instance_id(meta_service->resource_mgr(), "2:ALBJLH4Q:m-n3qdpyal27rh8iprxx");
ASSERT_EQ(instance_id, "");
// degraded format err
instance_id = get_instance_id(meta_service->resource_mgr(), "1:ALBJLH4Q");
ASSERT_EQ(instance_id, "");
// std::invalid_argument
instance_id = get_instance_id(meta_service->resource_mgr(),
"invalid_version:ALBJLH4Q:m-n3qdpyal27rh8iprxx");
ASSERT_EQ(instance_id, "");
// std::out_of_range
instance_id = get_instance_id(meta_service->resource_mgr(),
"12345678901:ALBJLH4Q:m-n3qdpyal27rh8iprxx");
ASSERT_EQ(instance_id, "");
config::enable_check_instance_id = true;
auto ms = get_meta_service(false);
instance_id =
get_instance_id(ms->resource_mgr(), "1:ALBJLH4Q-check-invalid:m-n3qdpyal27rh8iprxx");
ASSERT_EQ(instance_id, "");
sp->set_call_back("is_instance_id_registered", [&](auto&& args) {
TxnErrorCode* c0 = try_any_cast<TxnErrorCode*>(args[0]);
*c0 = TxnErrorCode::TXN_OK;
});
instance_id =
get_instance_id(ms->resource_mgr(), "1:ALBJLH4Q-check-invalid:m-n3qdpyal27rh8iprxx");
ASSERT_EQ(instance_id, "ALBJLH4Q-check-invalid");
config::enable_check_instance_id = false;
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
TEST(MetaServiceTest, CreateInstanceTest) {
auto meta_service = get_meta_service();
// case: normal create instance with obj info
{
brpc::Controller cntl;
CreateInstanceRequest req;
req.set_instance_id("test_instance");
req.set_user_id("test_user");
req.set_name("test_name");
ObjectStoreInfoPB obj;
obj.set_ak("123");
obj.set_sk("321");
obj.set_bucket("456");
obj.set_prefix("654");
obj.set_endpoint("789");
obj.set_region("987");
obj.set_external_endpoint("888");
obj.set_provider(ObjectStoreInfoPB::BOS);
req.mutable_obj_info()->CopyFrom(obj);
auto sp = SyncPoint::get_instance();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->enable_processing();
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
// case: normal create instance without obj info and hdfs info
{
brpc::Controller cntl;
CreateInstanceRequest req;
req.set_instance_id("test_instance_without_hdfs_and_obj");
req.set_user_id("test_user");
req.set_name("test_name");
auto sp = SyncPoint::get_instance();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->enable_processing();
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
// case: normal create instance with hdfs info
{
brpc::Controller cntl;
CreateInstanceRequest req;
req.set_instance_id("test_instance_with_hdfs_info");
req.set_user_id("test_user");
req.set_name("test_name");
HdfsVaultInfo hdfs;
HdfsBuildConf conf;
conf.set_fs_name("hdfs://127.0.0.1:8020");
conf.set_user("test_user");
hdfs.mutable_build_conf()->CopyFrom(conf);
StorageVaultPB vault;
vault.mutable_hdfs_info()->CopyFrom(hdfs);
req.mutable_vault()->CopyFrom(vault);
auto sp = SyncPoint::get_instance();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->enable_processing();
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
// case: request has invalid argument
{
brpc::Controller cntl;
CreateInstanceRequest req;
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// case: normal drop instance
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::DROP);
req.set_instance_id("test_instance");
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
InstanceKeyInfo key_info {"test_instance"};
std::string key;
std::string val;
instance_key(key_info, &key);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
InstanceInfoPB instance;
instance.ParseFromString(val);
ASSERT_EQ(instance.status(), InstanceInfoPB::DELETED);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// case: normal refresh instance
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::REFRESH);
req.set_instance_id("test_instance");
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// case: rpc get instance
{
brpc::Controller cntl;
GetInstanceRequest req;
GetInstanceResponse res;
meta_service->get_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
req.set_cloud_unique_id("1:test_instance:m-n3qdpyal27rh8iprxx");
meta_service->get_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
}
TEST(MetaServiceTest, AlterS3StorageVaultTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::pair<std::string, std::string> pair;
sp->set_call_back("extract_object_storage_info:get_aksk_pair", [&](auto&& args) {
auto* ret = try_any_cast<std::pair<std::string, std::string>*>(args[0]);
pair = *ret;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ObjectStoreInfoPB obj_info;
obj_info.set_id("1");
obj_info.set_ak("ak");
obj_info.set_sk("sk");
StorageVaultPB vault;
constexpr char vault_name[] = "test_alter_s3_vault";
vault.mutable_obj_info()->MergeFrom(obj_info);
vault.set_name(vault_name);
vault.set_id("2");
InstanceInfoPB instance;
instance.add_storage_vault_names(vault.name());
instance.add_resource_ids(vault.id());
instance.set_instance_id("GetObjStoreInfoTestInstance");
val = instance.SerializeAsString();
txn->put(key, val);
txn->put(storage_vault_key({instance.instance_id(), "2"}), vault.SerializeAsString());
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
txn = nullptr;
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_S3_VAULT);
StorageVaultPB vault;
vault.mutable_obj_info()->set_ak("new_ak");
vault.set_name(vault_name);
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_NE(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string val;
ASSERT_EQ(txn->get(storage_vault_key({instance.instance_id(), "2"}), &val),
TxnErrorCode::TXN_OK);
StorageVaultPB get_obj;
get_obj.ParseFromString(val);
ASSERT_EQ(get_obj.obj_info().ak(), "ak") << get_obj.obj_info().ak();
}
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_S3_VAULT);
StorageVaultPB vault;
ObjectStoreInfoPB obj;
obj_info.set_ak("new_ak");
vault.mutable_obj_info()->MergeFrom(obj);
vault.set_name("test_alter_s3_vault_non_exist");
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_NE(res.status().code(), MetaServiceCode::OK) << res.status().msg();
}
{
AlterObjStoreInfoRequest req;
constexpr char new_vault_name[] = "@!#vault_name";
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_S3_VAULT);
StorageVaultPB vault;
vault.set_alter_name(new_vault_name);
ObjectStoreInfoPB obj;
obj_info.set_ak("new_ak");
vault.mutable_obj_info()->MergeFrom(obj);
vault.set_name(vault_name);
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_NE(res.status().code(), MetaServiceCode::OK) << res.status().msg();
ASSERT_TRUE(res.status().msg().find("invalid storage vault name") != std::string::npos)
<< res.status().msg();
}
{
AlterObjStoreInfoRequest req;
constexpr char new_vault_name[] = "new_test_alter_s3_vault";
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_S3_VAULT);
StorageVaultPB vault;
vault.set_alter_name(new_vault_name);
ObjectStoreInfoPB obj;
obj_info.set_ak("new_ak");
obj_info.set_sk("new_sk");
vault.mutable_obj_info()->MergeFrom(obj);
vault.set_name(vault_name);
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_S3_VAULT);
StorageVaultPB vault;
vault.set_alter_name(new_vault_name);
ObjectStoreInfoPB obj;
obj_info.set_ak("new_ak");
obj_info.set_sk("new_sk");
vault.mutable_obj_info()->MergeFrom(obj);
vault.set_name(new_vault_name);
req.mutable_vault()->CopyFrom(vault);
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::ALREADY_EXISTED) << res.status().msg();
}
InstanceInfoPB instance;
get_test_instance(instance);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string val;
ASSERT_EQ(txn->get(storage_vault_key({instance.instance_id(), "2"}), &val),
TxnErrorCode::TXN_OK);
StorageVaultPB get_obj;
get_obj.ParseFromString(val);
ASSERT_EQ(get_obj.name(), new_vault_name) << get_obj.obj_info().ak();
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, AlterHdfsStorageVaultTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::pair<std::string, std::string> pair;
sp->set_call_back("extract_object_storage_info:get_aksk_pair", [&](auto&& args) {
auto* ret = try_any_cast<std::pair<std::string, std::string>*>(args[0]);
pair = *ret;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
HdfsBuildConf hdfs_build_conf;
hdfs_build_conf.set_fs_name("fs_name");
hdfs_build_conf.set_user("root");
HdfsVaultInfo hdfs_info;
hdfs_info.set_prefix("root_path");
hdfs_info.mutable_build_conf()->MergeFrom(hdfs_build_conf);
StorageVaultPB vault;
constexpr char vault_name[] = "test_alter_hdfs_vault";
vault.mutable_hdfs_info()->MergeFrom(hdfs_info);
vault.set_name(vault_name);
vault.set_id("2");
InstanceInfoPB instance;
instance.add_storage_vault_names(vault.name());
instance.add_resource_ids(vault.id());
instance.set_instance_id("GetObjStoreInfoTestInstance");
val = instance.SerializeAsString();
txn->put(key, val);
txn->put(storage_vault_key({instance.instance_id(), "2"}), vault.SerializeAsString());
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
txn = nullptr;
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_HDFS_VAULT);
StorageVaultPB vault;
vault.mutable_hdfs_info()->mutable_build_conf()->set_user("hadoop");
vault.set_name(vault_name);
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string val;
ASSERT_EQ(txn->get(storage_vault_key({instance.instance_id(), "2"}), &val),
TxnErrorCode::TXN_OK);
StorageVaultPB get_obj;
get_obj.ParseFromString(val);
ASSERT_EQ(get_obj.hdfs_info().build_conf().user(), "hadoop")
<< get_obj.hdfs_info().build_conf().fs_name();
}
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_HDFS_VAULT);
StorageVaultPB vault;
auto* hdfs = vault.mutable_hdfs_info();
hdfs->set_prefix("fake_one");
vault.set_name("test_alter_hdfs_vault_non_exist");
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_NE(res.status().code(), MetaServiceCode::OK) << res.status().msg();
}
{
AlterObjStoreInfoRequest req;
constexpr char new_vault_name[] = "Thi213***@fakeVault";
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_HDFS_VAULT);
StorageVaultPB vault;
vault.mutable_hdfs_info()->mutable_build_conf()->set_user("hadoop");
vault.set_name(vault_name);
vault.set_alter_name(new_vault_name);
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_NE(res.status().code(), MetaServiceCode::OK) << res.status().msg();
ASSERT_TRUE(res.status().msg().find("invalid storage vault name") != std::string::npos)
<< res.status().msg();
}
{
AlterObjStoreInfoRequest req;
constexpr char new_vault_name[] = "new_test_alter_hdfs_vault";
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_HDFS_VAULT);
StorageVaultPB vault;
vault.mutable_hdfs_info()->mutable_build_conf()->set_user("hadoop");
vault.set_name(vault_name);
vault.set_alter_name(new_vault_name);
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_HDFS_VAULT);
StorageVaultPB vault;
vault.mutable_hdfs_info()->mutable_build_conf()->set_user("hadoop");
vault.set_name(new_vault_name);
vault.set_alter_name(new_vault_name);
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::ALREADY_EXISTED) << res.status().msg();
}
InstanceInfoPB instance;
get_test_instance(instance);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string val;
ASSERT_EQ(txn->get(storage_vault_key({instance.instance_id(), "2"}), &val),
TxnErrorCode::TXN_OK);
StorageVaultPB get_obj;
get_obj.ParseFromString(val);
ASSERT_EQ(get_obj.name(), new_vault_name) << get_obj.obj_info().ak();
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, AlterClusterTest) {
auto meta_service = get_meta_service();
ASSERT_NE(meta_service, nullptr);
// case: normal add cluster
{
brpc::Controller cntl;
AlterClusterRequest req;
req.set_instance_id(mock_instance);
req.mutable_cluster()->set_cluster_name(mock_cluster_name);
req.set_op(AlterClusterRequest::ADD_CLUSTER);
AlterClusterResponse res;
meta_service->alter_cluster(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// case: request has invalid argument
{
brpc::Controller cntl;
AlterClusterRequest req;
req.set_op(AlterClusterRequest::DROP_CLUSTER);
AlterClusterResponse res;
meta_service->alter_cluster(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// add node
{
brpc::Controller cntl;
AlterClusterRequest req;
req.set_instance_id(mock_instance);
req.set_op(AlterClusterRequest::ADD_NODE);
req.mutable_cluster()->set_cluster_name(mock_cluster_name);
req.mutable_cluster()->set_cluster_id(mock_cluster_id);
req.mutable_cluster()->set_type(ClusterPB::COMPUTE);
auto node = req.mutable_cluster()->add_nodes();
node->set_ip("127.0.0.1");
node->set_heartbeat_port(9999);
AlterClusterResponse res;
meta_service->alter_cluster(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// drop node
{
brpc::Controller cntl;
AlterClusterRequest req;
req.set_instance_id(mock_instance);
req.set_op(AlterClusterRequest::DROP_NODE);
req.mutable_cluster()->set_cluster_name(mock_cluster_name);
req.mutable_cluster()->set_cluster_id(mock_cluster_id);
req.mutable_cluster()->set_type(ClusterPB::COMPUTE);
auto node = req.mutable_cluster()->add_nodes();
node->set_ip("127.0.0.1");
node->set_heartbeat_port(9999);
AlterClusterResponse res;
meta_service->alter_cluster(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// rename cluster
{
brpc::Controller cntl;
AlterClusterRequest req;
req.set_instance_id(mock_instance);
req.mutable_cluster()->set_cluster_id(mock_cluster_id);
req.mutable_cluster()->set_cluster_name("rename_cluster_name");
req.set_op(AlterClusterRequest::RENAME_CLUSTER);
AlterClusterResponse res;
meta_service->alter_cluster(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// set cluster status
{
brpc::Controller cntl;
AlterClusterRequest req;
req.set_instance_id(mock_instance);
req.mutable_cluster()->set_cluster_id(mock_cluster_id);
req.mutable_cluster()->set_cluster_status(ClusterStatus::SUSPENDED);
req.set_op(AlterClusterRequest::SET_CLUSTER_STATUS);
AlterClusterResponse res;
meta_service->alter_cluster(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// set UPDATE_CLUSTER_MYSQL_USER_NAME
{
brpc::Controller cntl;
AlterClusterRequest req;
req.set_instance_id(mock_instance);
req.mutable_cluster()->set_cluster_id(mock_cluster_id);
req.mutable_cluster()->add_mysql_user_name("test_user");
req.set_op(AlterClusterRequest::UPDATE_CLUSTER_MYSQL_USER_NAME);
AlterClusterResponse res;
meta_service->alter_cluster(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
}
TEST(MetaServiceTest, GetClusterTest) {
auto meta_service = get_meta_service();
// add cluster first
InstanceKeyInfo key_info {mock_instance};
std::string key;
std::string val;
instance_key(key_info, &key);
InstanceInfoPB instance;
instance.set_instance_id(mock_instance);
ClusterPB c1;
c1.set_cluster_name(mock_cluster_name);
c1.set_cluster_id(mock_cluster_id);
c1.add_mysql_user_name()->append("m1");
instance.add_clusters()->CopyFrom(c1);
val = instance.SerializeAsString();
std::unique_ptr<Transaction> txn;
std::string get_val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
// case: normal get
{
brpc::Controller cntl;
GetClusterRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_cluster_id(mock_cluster_id);
req.set_cluster_name("test_cluster");
GetClusterResponse res;
meta_service->get_cluster(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
}
TEST(MetaServiceTest, BeginTxnTest) {
auto meta_service = get_meta_service();
int64_t db_id = 666;
int64_t table_id = 123;
const std::string& label = "test_label";
int64_t timeout_ms = 60 * 1000;
// test invalid argument
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.add_table_ids(table_id);
txn_info.set_timeout_ms(timeout_ms);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(label);
txn_info.add_table_ids(table_id);
txn_info.set_timeout_ms(timeout_ms);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// case: label already used
{
brpc::Controller cntl;
BeginTxnRequest req;
auto label_already_in_use = "test_label_already_in_use";
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info;
txn_info.set_db_id(888);
txn_info.set_label(label_already_in_use);
txn_info.add_table_ids(456);
txn_info.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::TXN_LABEL_ALREADY_USED);
auto found = res.status().msg().find(fmt::format(
"Label [{}] has already been used, relate to txn", label_already_in_use));
ASSERT_NE(found, std::string::npos);
}
// case: dup begin txn request
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info;
txn_info.set_db_id(999);
txn_info.set_label("test_label_dup_request");
txn_info.add_table_ids(789);
UniqueIdPB unique_id_pb;
unique_id_pb.set_hi(100);
unique_id_pb.set_lo(10);
txn_info.mutable_request_id()->CopyFrom(unique_id_pb);
txn_info.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::TXN_DUPLICATED_REQ);
}
{
// ===========================================================================
// threads concurrent execution with sequence in begin_txn with same label:
//
// thread1 thread2
// | |
// | commit_txn1
// | |
// | |
// | |
// commit_txn2 |
// | |
// v v
//
std::mutex go_mutex;
std::condition_variable go_cv;
bool go = false;
auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
std::atomic<int32_t> count_txn1 = {0};
std::atomic<int32_t> count_txn2 = {0};
std::atomic<int32_t> count_txn3 = {0};
int64_t db_id = 1928354123;
int64_t table_id = 12131231231;
std::string test_label = "test_race_with_same_label";
std::atomic<int32_t> success_txn = {0};
sp->set_call_back("begin_txn:before:commit_txn:1", [&](auto&& args) {
const auto& label = *try_any_cast<std::string*>(args[0]);
std::unique_lock<std::mutex> _lock(go_mutex);
count_txn1++;
LOG(INFO) << "count_txn1:" << count_txn1 << " label=" << label;
if (count_txn1 == 1) {
{
LOG(INFO) << "count_txn1:" << count_txn1 << " label=" << label << " go=" << go;
go_cv.wait(_lock);
}
}
if (count_txn1 == 2) {
{
LOG(INFO) << "count_txn1:" << count_txn1 << " label=" << label << " go=" << go;
go_cv.notify_all();
}
}
});
sp->set_call_back("begin_txn:after:commit_txn:1", [&](auto&& args) {
const auto& label = *try_any_cast<std::string*>(args[0]);
std::unique_lock<std::mutex> _lock(go_mutex);
count_txn2++;
LOG(INFO) << "count_txn2:" << count_txn2 << " label=" << label;
if (count_txn2 == 1) {
{
LOG(INFO) << "count_txn2:" << count_txn2 << " label=" << label << " go=" << go;
go_cv.wait(_lock);
}
}
if (count_txn2 == 2) {
{
LOG(INFO) << "count_txn2:" << count_txn2 << " label=" << label << " go=" << go;
go_cv.notify_all();
}
}
});
sp->set_call_back("begin_txn:after:commit_txn:2", [&](auto&& args) {
int64_t txn_id = *try_any_cast<int64_t*>(args[0]);
count_txn3++;
LOG(INFO) << "count_txn3:" << count_txn3 << " txn_id=" << txn_id;
});
sp->enable_processing();
std::thread thread1([&] {
{
std::unique_lock<std::mutex> _lock(go_mutex);
go_cv.wait(_lock, [&] { return go; });
}
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(test_label);
txn_info.add_table_ids(table_id);
UniqueIdPB unique_id_pb;
unique_id_pb.set_hi(1001);
unique_id_pb.set_lo(11);
txn_info.mutable_request_id()->CopyFrom(unique_id_pb);
txn_info.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
if (res.status().code() == MetaServiceCode::OK) {
success_txn++;
} else {
ASSERT_EQ(res.status().code(), MetaServiceCode::KV_TXN_CONFLICT);
}
});
std::thread thread2([&] {
{
std::unique_lock<std::mutex> _lock(go_mutex);
go_cv.wait(_lock, [&] { return go; });
}
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(test_label);
txn_info.add_table_ids(table_id);
UniqueIdPB unique_id_pb;
unique_id_pb.set_hi(100);
unique_id_pb.set_lo(10);
txn_info.mutable_request_id()->CopyFrom(unique_id_pb);
txn_info.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
if (res.status().code() == MetaServiceCode::OK) {
success_txn++;
} else {
ASSERT_EQ(res.status().code(), MetaServiceCode::KV_TXN_CONFLICT);
}
});
std::unique_lock<std::mutex> go_lock(go_mutex);
go = true;
go_lock.unlock();
go_cv.notify_all();
thread1.join();
thread2.join();
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
ASSERT_EQ(success_txn.load(), 1);
}
{
// ===========================================================================
// threads concurrent execution with sequence in begin_txn with different label:
//
// thread1 thread2
// | |
// | commit_txn1
// | |
// | |
// | |
// commit_txn2 |
// | |
// v v
std::mutex go_mutex;
std::condition_variable go_cv;
bool go = false;
auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
std::atomic<int32_t> count_txn1 = {0};
std::atomic<int32_t> count_txn2 = {0};
std::mutex flow_mutex_1;
std::condition_variable flow_cv_1;
int64_t db_id = 19541231112;
int64_t table_id = 312312321211;
std::string test_label1 = "test_race_with_diff_label1";
std::string test_label2 = "test_race_with_diff_label2";
std::atomic<int32_t> success_txn = {0};
sp->set_call_back("begin_txn:before:commit_txn:1", [&](auto&& args) {
std::string label = *try_any_cast<std::string*>(args[0]);
if (count_txn1.load() == 1) {
std::unique_lock<std::mutex> flow_lock_1(flow_mutex_1);
flow_cv_1.wait(flow_lock_1);
}
count_txn1++;
LOG(INFO) << "count_txn1:" << count_txn1 << " label=" << label;
});
sp->set_call_back("begin_txn:after:commit_txn:2", [&](auto&& args) {
int64_t txn_id = *try_any_cast<int64_t*>(args[0]);
while (count_txn2.load() == 0 && count_txn1.load() == 1) {
sleep(1);
flow_cv_1.notify_all();
}
count_txn2++;
LOG(INFO) << "count_txn2:" << count_txn2 << " txn_id=" << txn_id;
});
sp->enable_processing();
std::thread thread1([&] {
{
std::unique_lock<std::mutex> _lock(go_mutex);
go_cv.wait(_lock, [&] { return go; });
}
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(test_label1);
txn_info.add_table_ids(table_id);
UniqueIdPB unique_id_pb;
unique_id_pb.set_hi(1001);
unique_id_pb.set_lo(11);
txn_info.mutable_request_id()->CopyFrom(unique_id_pb);
txn_info.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
if (res.status().code() == MetaServiceCode::OK) {
success_txn++;
} else {
ASSERT_EQ(res.status().code(), MetaServiceCode::TXN_LABEL_ALREADY_USED);
}
});
std::thread thread2([&] {
{
std::unique_lock<std::mutex> _lock(go_mutex);
go_cv.wait(_lock, [&] { return go; });
}
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(test_label2);
txn_info.add_table_ids(table_id);
txn_info.set_timeout_ms(36000);
UniqueIdPB unique_id_pb;
unique_id_pb.set_hi(100);
unique_id_pb.set_lo(10);
txn_info.mutable_request_id()->CopyFrom(unique_id_pb);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
if (res.status().code() == MetaServiceCode::OK) {
success_txn++;
} else {
ASSERT_EQ(res.status().code(), MetaServiceCode::TXN_LABEL_ALREADY_USED);
}
});
std::unique_lock<std::mutex> go_lock(go_mutex);
go = true;
go_lock.unlock();
go_cv.notify_all();
thread1.join();
thread2.join();
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
ASSERT_EQ(success_txn.load(), 2);
}
{
// test reuse label
// 1. beigin_txn
// 2. abort_txn
// 3. begin_txn again can successfully
std::string cloud_unique_id = "test_cloud_unique_id";
int64_t db_id = 124343989;
int64_t table_id = 1231311;
int64_t txn_id = -1;
std::string label = "test_reuse_label";
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id(cloud_unique_id);
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(label);
txn_info.add_table_ids(table_id);
txn_info.set_timeout_ms(36000);
UniqueIdPB unique_id_pb;
unique_id_pb.set_hi(100);
unique_id_pb.set_lo(10);
txn_info.mutable_request_id()->CopyFrom(unique_id_pb);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// abort txn
{
brpc::Controller cntl;
AbortTxnRequest req;
req.set_cloud_unique_id(cloud_unique_id);
ASSERT_GT(txn_id, 0);
req.set_txn_id(txn_id);
req.set_reason("test");
AbortTxnResponse res;
meta_service->abort_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().status(), TxnStatusPB::TXN_STATUS_ABORTED);
}
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id(cloud_unique_id);
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(label);
txn_info.add_table_ids(table_id);
UniqueIdPB unique_id_pb;
unique_id_pb.set_hi(100);
unique_id_pb.set_lo(10);
txn_info.mutable_request_id()->CopyFrom(unique_id_pb);
txn_info.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_GT(res.txn_id(), txn_id);
}
}
{
// test reuse label exceed max_num_aborted_txn
std::string cloud_unique_id = "test_cloud_unique_id";
int64_t db_id = 124343989;
int64_t table_id = 12897811;
int64_t txn_id = -1;
std::string label = "test_max_num_aborted_txn_label";
for (int i = 0; i < config::max_num_aborted_txn; i++) {
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id(cloud_unique_id);
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(label);
txn_info.add_table_ids(table_id);
txn_info.set_timeout_ms(36000);
UniqueIdPB unique_id_pb;
unique_id_pb.set_hi(100);
unique_id_pb.set_lo(10);
txn_info.mutable_request_id()->CopyFrom(unique_id_pb);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// abort txn
{
brpc::Controller cntl;
AbortTxnRequest req;
req.set_cloud_unique_id(cloud_unique_id);
ASSERT_GT(txn_id, 0);
req.set_txn_id(txn_id);
req.set_reason("test");
AbortTxnResponse res;
meta_service->abort_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().status(), TxnStatusPB::TXN_STATUS_ABORTED);
}
}
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id(cloud_unique_id);
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(label);
txn_info.add_table_ids(table_id);
UniqueIdPB unique_id_pb;
unique_id_pb.set_hi(100);
unique_id_pb.set_lo(10);
txn_info.mutable_request_id()->CopyFrom(unique_id_pb);
txn_info.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_TRUE(res.status().msg().find("too many aborted txn for label") !=
std::string::npos);
}
}
}
TEST(MetaServiceTest, PrecommitTest1) {
// PrecommitTestCase1: only use db_id for precommit_txn
auto meta_service = get_meta_service();
const int64_t db_id = 563413;
const int64_t table_id = 417417878;
const std::string& label = "label_123dae121das";
int64_t txn_id = -1;
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(label);
txn_info.add_table_ids(table_id);
txn_info.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
ASSERT_GT(txn_id, -1);
}
{
brpc::Controller cntl;
PrecommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_precommit_timeout_ms(36000);
PrecommitTxnResponse res;
meta_service->precommit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
{
std::unique_ptr<Transaction> txn;
TxnErrorCode err = meta_service->txn_kv()->create_txn(&txn);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
const std::string info_key = txn_info_key({mock_instance, db_id, txn_id});
std::string info_val;
ASSERT_EQ(txn->get(info_key, &info_val), TxnErrorCode::TXN_OK);
TxnInfoPB txn_info;
txn_info.ParseFromString(info_val);
ASSERT_EQ(txn_info.status(), TxnStatusPB::TXN_STATUS_PREPARED);
brpc::Controller cntl;
PrecommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
req.set_precommit_timeout_ms(36000);
PrecommitTxnResponse res;
meta_service->precommit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
err = meta_service->txn_kv()->create_txn(&txn);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(info_key, &info_val), TxnErrorCode::TXN_OK);
txn_info.ParseFromString(info_val);
ASSERT_EQ(txn_info.status(), TxnStatusPB::TXN_STATUS_PRECOMMITTED);
}
}
TEST(MetaServiceTest, PrecommitTxnTest2) {
auto meta_service = get_meta_service();
const int64_t db_id = 563413;
const int64_t table_id = 417417878;
const std::string& label = "label_123dae121das";
int64_t txn_id = -1;
// begin txn first
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info;
txn_info.set_db_id(db_id);
txn_info.set_label(label);
txn_info.add_table_ids(table_id);
txn_info.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
ASSERT_GT(txn_id, -1);
}
// case: txn's status should be TXN_STATUS_PRECOMMITTED
{
std::unique_ptr<Transaction> txn;
TxnErrorCode err = meta_service->txn_kv()->create_txn(&txn);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
const std::string info_key = txn_info_key({mock_instance, db_id, txn_id});
std::string info_val;
ASSERT_EQ(txn->get(info_key, &info_val), TxnErrorCode::TXN_OK);
TxnInfoPB txn_info;
txn_info.ParseFromString(info_val);
// before call precommit_txn, txn's status is TXN_STATUS_PREPARED
ASSERT_EQ(txn_info.status(), TxnStatusPB::TXN_STATUS_PREPARED);
brpc::Controller cntl;
PrecommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(txn_id);
req.set_precommit_timeout_ms(36000);
PrecommitTxnResponse res;
meta_service->precommit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
err = meta_service->txn_kv()->create_txn(&txn);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(info_key, &info_val), TxnErrorCode::TXN_OK);
txn_info.ParseFromString(info_val);
// after call precommit_txn, txn's status is TXN_STATUS_PRECOMMITTED
ASSERT_EQ(txn_info.status(), TxnStatusPB::TXN_STATUS_PRECOMMITTED);
}
// case: when txn's status is TXN_STATUS_ABORTED/TXN_STATUS_VISIBLE/TXN_STATUS_PRECOMMITTED
{
// TXN_STATUS_ABORTED
std::unique_ptr<Transaction> txn;
TxnErrorCode err = meta_service->txn_kv()->create_txn(&txn);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
const std::string info_key = txn_info_key({mock_instance, db_id, txn_id});
std::string info_val;
ASSERT_EQ(txn->get(info_key, &info_val), TxnErrorCode::TXN_OK);
TxnInfoPB txn_info;
txn_info.ParseFromString(info_val);
txn_info.set_status(TxnStatusPB::TXN_STATUS_ABORTED);
info_val.clear();
txn_info.SerializeToString(&info_val);
txn->put(info_key, info_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
brpc::Controller cntl;
PrecommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(txn_id);
req.set_precommit_timeout_ms(36000);
PrecommitTxnResponse res;
meta_service->precommit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::TXN_ALREADY_ABORTED);
// TXN_STATUS_VISIBLE
txn_info.set_status(TxnStatusPB::TXN_STATUS_VISIBLE);
info_val.clear();
txn_info.SerializeToString(&info_val);
txn->put(info_key, info_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
meta_service->precommit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::TXN_ALREADY_VISIBLE);
// TXN_STATUS_PRECOMMITTED
txn_info.set_status(TxnStatusPB::TXN_STATUS_PRECOMMITTED);
info_val.clear();
txn_info.SerializeToString(&info_val);
txn->put(info_key, info_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
meta_service->precommit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::TXN_ALREADY_PRECOMMITED);
}
}
TEST(MetaServiceTest, CommitTxnTest) {
auto meta_service = get_meta_service();
int64_t table_id = 1234;
int64_t index_id = 1235;
int64_t partition_id = 1236;
// case: first version of rowset
{
int64_t txn_id = -1;
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(666);
txn_info_pb.set_label("test_label");
txn_info_pb.add_table_ids(1234);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// mock rowset and tablet
int64_t tablet_id_base = 1103;
for (int i = 0; i < 5; ++i) {
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id_base + i);
auto tmp_rowset = create_rowset(txn_id, tablet_id_base + i, partition_id);
CreateRowsetResponse res;
commit_rowset(meta_service.get(), tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// precommit txn
{
brpc::Controller cntl;
PrecommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(666);
req.set_txn_id(txn_id);
req.set_precommit_timeout_ms(36000);
PrecommitTxnResponse res;
meta_service->precommit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// commit txn
{
brpc::Controller cntl;
CommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(666);
req.set_txn_id(txn_id);
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// doubly commit txn
{
brpc::Controller cntl;
CommitTxnRequest req;
auto db_id = 666;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(txn_id);
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
auto found = res.status().msg().find(fmt::format(
"transaction is already visible: db_id={} txn_id={}", db_id, txn_id));
ASSERT_TRUE(found != std::string::npos);
}
// doubly commit txn(2pc)
{
brpc::Controller cntl;
CommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(666);
req.set_txn_id(txn_id);
req.set_is_2pc(true);
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::TXN_ALREADY_VISIBLE);
auto found = res.status().msg().find(
fmt::format("transaction [{}] is already visible, not pre-committed.", txn_id));
ASSERT_TRUE(found != std::string::npos);
}
}
}
TEST(MetaServiceTest, CommitTxnExpiredTest) {
auto meta_service = get_meta_service();
int64_t table_id = 1234789234;
int64_t index_id = 1235;
int64_t partition_id = 1236;
// case: first version of rowset
{
int64_t txn_id = -1;
int64_t db_id = 713232132;
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label("test_commit_txn_expired");
txn_info_pb.add_table_ids(1234789234);
txn_info_pb.set_timeout_ms(1);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// mock rowset and tablet
int64_t tablet_id_base = 1103;
for (int i = 0; i < 5; ++i) {
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id_base + i);
auto tmp_rowset = create_rowset(txn_id, tablet_id_base + i, partition_id);
CreateRowsetResponse res;
commit_rowset(meta_service.get(), tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// sleep 1 second for txn timeout
sleep(1);
// commit txn
{
brpc::Controller cntl;
CommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(txn_id);
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::UNDEFINED_ERR);
ASSERT_TRUE(res.status().msg().find("txn is expired, not allow to commit txn_id=") !=
std::string::npos);
}
}
}
void create_and_commit_rowset(MetaServiceProxy* meta_service, int64_t table_id, int64_t index_id,
int64_t partition_id, int64_t tablet_id, int64_t txn_id) {
create_tablet(meta_service, table_id, index_id, partition_id, tablet_id);
auto tmp_rowset = create_rowset(txn_id, tablet_id, partition_id);
CreateRowsetResponse res;
commit_rowset(meta_service, tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
TEST(MetaServiceTest, CommitTxnWithSubTxnTest) {
auto meta_service = get_meta_service();
int64_t db_id = 98131;
int64_t txn_id = -1;
int64_t t1 = 10;
int64_t t1_index = 100;
int64_t t1_p1 = 11;
int64_t t1_p1_t1 = 12;
int64_t t1_p1_t2 = 13;
int64_t t1_p2 = 14;
int64_t t1_p2_t1 = 15;
int64_t t2 = 16;
int64_t t2_index = 101;
int64_t t2_p3 = 17;
int64_t t2_p3_t1 = 18;
[[maybe_unused]] int64_t t2_p4 = 19;
[[maybe_unused]] int64_t t2_p4_t1 = 20;
std::string label = "test_label";
std::string label2 = "test_label_0";
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(t1);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// mock rowset and tablet: for sub_txn1
int64_t sub_txn_id1 = txn_id;
create_and_commit_rowset(meta_service.get(), t1, t1_index, t1_p1, t1_p1_t1, sub_txn_id1);
create_and_commit_rowset(meta_service.get(), t1, t1_index, t1_p1, t1_p1_t2, sub_txn_id1);
create_and_commit_rowset(meta_service.get(), t1, t1_index, t1_p2, t1_p2_t1, sub_txn_id1);
// begin_sub_txn2
int64_t sub_txn_id2 = -1;
{
brpc::Controller cntl;
BeginSubTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
req.set_sub_txn_num(0);
req.set_db_id(db_id);
req.set_label(label2);
req.mutable_table_ids()->Add(t1);
req.mutable_table_ids()->Add(t2);
BeginSubTxnResponse res;
meta_service->begin_sub_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().table_ids().size(), 2);
ASSERT_EQ(res.txn_info().sub_txn_ids().size(), 1);
ASSERT_TRUE(res.has_sub_txn_id());
sub_txn_id2 = res.sub_txn_id();
ASSERT_EQ(sub_txn_id2, res.txn_info().sub_txn_ids()[0]);
}
// mock rowset and tablet: for sub_txn3
create_and_commit_rowset(meta_service.get(), t2, t2_index, t2_p3, t2_p3_t1, sub_txn_id2);
// begin_sub_txn3
int64_t sub_txn_id3 = -1;
{
brpc::Controller cntl;
BeginSubTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
req.set_sub_txn_num(1);
req.set_db_id(db_id);
req.set_label("test_label_1");
req.mutable_table_ids()->Add(t1);
req.mutable_table_ids()->Add(t2);
req.mutable_table_ids()->Add(t1);
BeginSubTxnResponse res;
meta_service->begin_sub_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().table_ids().size(), 3);
ASSERT_EQ(res.txn_info().sub_txn_ids().size(), 2);
ASSERT_TRUE(res.has_sub_txn_id());
sub_txn_id3 = res.sub_txn_id();
ASSERT_EQ(sub_txn_id3, res.txn_info().sub_txn_ids()[1]);
}
// mock rowset and tablet: for sub_txn3
create_and_commit_rowset(meta_service.get(), t1, t1_index, t1_p1, t1_p1_t1, sub_txn_id3);
create_and_commit_rowset(meta_service.get(), t1, t1_index, t1_p1, t1_p1_t2, sub_txn_id3);
// commit txn
CommitTxnRequest req;
{
brpc::Controller cntl;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(666);
req.set_txn_id(txn_id);
req.set_is_txn_load(true);
SubTxnInfo sub_txn_info1;
sub_txn_info1.set_sub_txn_id(sub_txn_id1);
sub_txn_info1.set_table_id(t1);
sub_txn_info1.mutable_base_tablet_ids()->Add(t1_p1_t1);
sub_txn_info1.mutable_base_tablet_ids()->Add(t1_p1_t2);
sub_txn_info1.mutable_base_tablet_ids()->Add(t1_p2_t1);
SubTxnInfo sub_txn_info2;
sub_txn_info2.set_sub_txn_id(sub_txn_id2);
sub_txn_info2.set_table_id(t2);
sub_txn_info2.mutable_base_tablet_ids()->Add(t2_p3_t1);
SubTxnInfo sub_txn_info3;
sub_txn_info3.set_sub_txn_id(sub_txn_id3);
sub_txn_info3.set_table_id(t1);
sub_txn_info3.mutable_base_tablet_ids()->Add(t1_p1_t1);
sub_txn_info3.mutable_base_tablet_ids()->Add(t1_p1_t2);
req.mutable_sub_txn_infos()->Add(std::move(sub_txn_info1));
req.mutable_sub_txn_infos()->Add(std::move(sub_txn_info2));
req.mutable_sub_txn_infos()->Add(std::move(sub_txn_info3));
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
// std::cout << res.DebugString() << std::endl;
ASSERT_EQ(res.table_ids().size(), 3);
ASSERT_EQ(res.table_ids()[0], t2);
ASSERT_EQ(res.partition_ids()[0], t2_p3);
ASSERT_EQ(res.versions()[0], 2);
ASSERT_EQ(res.table_ids()[1], t1);
ASSERT_EQ(res.partition_ids()[1], t1_p2);
ASSERT_EQ(res.versions()[1], 2);
ASSERT_EQ(res.table_ids()[2], t1);
ASSERT_EQ(res.partition_ids()[2], t1_p1) << res.ShortDebugString();
ASSERT_EQ(res.versions()[2], 3) << res.ShortDebugString();
}
// doubly commit txn
{
brpc::Controller cntl;
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
auto found = res.status().msg().find(
fmt::format("transaction is already visible: db_id={} txn_id={}", db_id, txn_id));
ASSERT_TRUE(found != std::string::npos);
}
// check kv
{
std::unique_ptr<Transaction> txn;
TxnErrorCode err = meta_service->txn_kv()->create_txn(&txn);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
// txn_info
std::string info_key = txn_info_key({mock_instance, db_id, txn_id});
std::string info_val;
ASSERT_EQ(txn->get(info_key, &info_val), TxnErrorCode::TXN_OK);
TxnInfoPB txn_info;
txn_info.ParseFromString(info_val);
ASSERT_EQ(txn_info.status(), TxnStatusPB::TXN_STATUS_VISIBLE);
info_key = txn_info_key({mock_instance, db_id, sub_txn_id2});
ASSERT_EQ(txn->get(info_key, &info_val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// txn_index
std::string index_key = txn_index_key({mock_instance, txn_id});
std::string index_val;
ASSERT_EQ(txn->get(index_key, &index_val), TxnErrorCode::TXN_OK);
TxnIndexPB txn_index;
txn_index.ParseFromString(index_val);
ASSERT_TRUE(txn_index.has_tablet_index());
index_key = txn_index_key({mock_instance, sub_txn_id3});
ASSERT_EQ(txn->get(index_key, &index_val), TxnErrorCode::TXN_OK);
txn_index.ParseFromString(index_val);
ASSERT_TRUE(txn_index.has_tablet_index());
// txn_label
std::string label_key = txn_label_key({mock_instance, db_id, label});
std::string label_val;
ASSERT_EQ(txn->get(label_key, &label_val), TxnErrorCode::TXN_OK);
label_key = txn_label_key({mock_instance, db_id, label2});
ASSERT_EQ(txn->get(label_key, &label_val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// txn_running
std::string running_key = txn_running_key({mock_instance, db_id, txn_id});
std::string running_val;
ASSERT_EQ(txn->get(running_key, &running_val), TxnErrorCode::TXN_KEY_NOT_FOUND);
running_key = txn_running_key({mock_instance, db_id, sub_txn_id3});
ASSERT_EQ(txn->get(running_key, &running_val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// tmp rowset
int64_t ids[] = {txn_id, sub_txn_id1, sub_txn_id2, sub_txn_id3};
for (auto id : ids) {
MetaRowsetTmpKeyInfo rs_tmp_key_info0 {mock_instance, id, 0};
MetaRowsetTmpKeyInfo rs_tmp_key_info1 {mock_instance, id + 1, 0};
std::string rs_tmp_key0;
std::string rs_tmp_key1;
meta_rowset_tmp_key(rs_tmp_key_info0, &rs_tmp_key0);
meta_rowset_tmp_key(rs_tmp_key_info1, &rs_tmp_key1);
std::unique_ptr<RangeGetIterator> it;
ASSERT_EQ(txn->get(rs_tmp_key0, rs_tmp_key1, &it, true), TxnErrorCode::TXN_OK);
ASSERT_FALSE(it->has_next());
}
// partition version
std::string ver_key = partition_version_key({mock_instance, db_id, t2, t2_p3});
std::string ver_val;
ASSERT_EQ(txn->get(ver_key, &ver_val), TxnErrorCode::TXN_OK);
VersionPB version;
version.ParseFromString(ver_val);
ASSERT_EQ(version.version(), 2);
ver_key = partition_version_key({mock_instance, db_id, t1, t1_p2});
ASSERT_EQ(txn->get(ver_key, &ver_val), TxnErrorCode::TXN_OK);
version.ParseFromString(ver_val);
ASSERT_EQ(version.version(), 2);
ver_key = partition_version_key({mock_instance, db_id, t1, t1_p1});
ASSERT_EQ(txn->get(ver_key, &ver_val), TxnErrorCode::TXN_OK);
version.ParseFromString(ver_val);
ASSERT_EQ(version.version(), 3);
// table version
std::string table_ver_key = table_version_key({mock_instance, db_id, t1});
std::string table_ver_val;
ASSERT_EQ(txn->get(table_ver_key, &table_ver_val), TxnErrorCode::TXN_OK);
auto val_int = *reinterpret_cast<const int64_t*>(table_ver_val.data());
ASSERT_EQ(val_int, 1);
table_version_key({mock_instance, db_id, t2});
ASSERT_EQ(txn->get(table_ver_key, &table_ver_val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(table_ver_val.data());
ASSERT_EQ(val_int, 1);
}
}
TEST(MetaServiceTest, CommitTxnWithSubTxnTest2) {
auto meta_service = get_meta_service();
int64_t db_id = 99131;
int64_t txn_id = -1;
int64_t t1 = 20;
int64_t t1_index = 200;
int64_t t1_p1 = 21;
int64_t t1_p1_t1 = 22;
int64_t t1_p1_t2 = 23;
int64_t t1_p2 = 24;
int64_t t1_p2_t1 = 25;
int64_t t2 = 26;
int64_t t2_index = 201;
int64_t t2_p3 = 27;
int64_t t2_p3_t1 = 28;
[[maybe_unused]] int64_t t2_p4 = 29;
[[maybe_unused]] int64_t t2_p4_t1 = 30;
std::string label = "test_label_10";
std::string label2 = "test_label_11";
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(t1);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
std::vector<SubTxnInfo> sub_txn_infos;
for (int i = 0; i < 500; i++) {
int64_t sub_txn_id1 = -1;
if (i == 0) {
sub_txn_id1 = txn_id;
} else {
brpc::Controller cntl;
BeginSubTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
req.set_sub_txn_num(sub_txn_infos.size() - 1);
req.set_db_id(db_id);
req.set_label(label2);
req.mutable_table_ids()->Add(t1);
if (i > 0) {
req.mutable_table_ids()->Add(t2);
}
BeginSubTxnResponse res;
meta_service->begin_sub_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().table_ids().size(), i == 0 ? 1 : 2);
ASSERT_EQ(res.txn_info().sub_txn_ids().size(), sub_txn_infos.size());
ASSERT_TRUE(res.has_sub_txn_id());
sub_txn_id1 = res.sub_txn_id();
ASSERT_EQ(sub_txn_id1,
res.txn_info().sub_txn_ids()[res.txn_info().sub_txn_ids().size() - 1]);
}
// mock rowset and tablet: for
create_and_commit_rowset(meta_service.get(), t1, t1_index, t1_p1, t1_p1_t1, sub_txn_id1);
create_and_commit_rowset(meta_service.get(), t1, t1_index, t1_p1, t1_p1_t2, sub_txn_id1);
create_and_commit_rowset(meta_service.get(), t1, t1_index, t1_p2, t1_p2_t1, sub_txn_id1);
// generate sub_txn_info
{
SubTxnInfo sub_txn_info1;
sub_txn_info1.set_sub_txn_id(sub_txn_id1);
sub_txn_info1.set_table_id(t1);
sub_txn_info1.mutable_base_tablet_ids()->Add(t1_p1_t1);
sub_txn_info1.mutable_base_tablet_ids()->Add(t1_p1_t2);
sub_txn_info1.mutable_base_tablet_ids()->Add(t1_p2_t1);
sub_txn_infos.push_back(sub_txn_info1);
}
// begin_sub_txn2
int64_t sub_txn_id2 = -1;
{
brpc::Controller cntl;
BeginSubTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
req.set_sub_txn_num(sub_txn_infos.size() - 1);
req.set_db_id(db_id);
req.set_label(label2);
req.mutable_table_ids()->Add(t1);
req.mutable_table_ids()->Add(t2);
BeginSubTxnResponse res;
meta_service->begin_sub_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().table_ids().size(), 2);
ASSERT_EQ(res.txn_info().sub_txn_ids().size(), sub_txn_infos.size());
ASSERT_TRUE(res.has_sub_txn_id());
sub_txn_id2 = res.sub_txn_id();
}
// mock rowset and tablet: for sub_txn3
create_and_commit_rowset(meta_service.get(), t2, t2_index, t2_p3, t2_p3_t1, sub_txn_id2);
{
SubTxnInfo sub_txn_info2;
sub_txn_info2.set_sub_txn_id(sub_txn_id2);
sub_txn_info2.set_table_id(t2);
sub_txn_info2.mutable_base_tablet_ids()->Add(t2_p3_t1);
sub_txn_infos.push_back(sub_txn_info2);
}
// begin_sub_txn3
int64_t sub_txn_id3 = -1;
{
brpc::Controller cntl;
BeginSubTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
req.set_sub_txn_num(sub_txn_infos.size() - 1);
req.set_db_id(db_id);
req.set_label(label2);
req.mutable_table_ids()->Add(t1);
req.mutable_table_ids()->Add(t2);
BeginSubTxnResponse res;
meta_service->begin_sub_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().table_ids().size(), 2);
ASSERT_EQ(res.txn_info().sub_txn_ids().size(), sub_txn_infos.size());
ASSERT_TRUE(res.has_sub_txn_id());
sub_txn_id3 = res.sub_txn_id();
}
// mock rowset and tablet: for sub_txn3
create_and_commit_rowset(meta_service.get(), t1, t1_index, t1_p1, t1_p1_t1, sub_txn_id3);
create_and_commit_rowset(meta_service.get(), t1, t1_index, t1_p1, t1_p1_t2, sub_txn_id3);
{
SubTxnInfo sub_txn_info3;
sub_txn_info3.set_sub_txn_id(sub_txn_id3);
sub_txn_info3.set_table_id(t1);
sub_txn_info3.mutable_base_tablet_ids()->Add(t1_p1_t1);
sub_txn_info3.mutable_base_tablet_ids()->Add(t1_p1_t2);
sub_txn_infos.push_back(sub_txn_info3);
}
}
// commit txn
CommitTxnRequest req;
{
brpc::Controller cntl;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(666);
req.set_txn_id(txn_id);
req.set_is_txn_load(true);
for (const auto& sub_txn_info : sub_txn_infos) {
req.add_sub_txn_infos()->CopyFrom(sub_txn_info);
}
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
std::cout << res.DebugString() << std::endl;
ASSERT_EQ(res.table_ids().size(), 3);
ASSERT_EQ(res.table_ids()[0], t2);
ASSERT_EQ(res.partition_ids()[0], t2_p3);
ASSERT_EQ(res.versions()[0], 501);
ASSERT_EQ(res.table_ids()[1], t1);
ASSERT_EQ(res.partition_ids()[1], t1_p2);
ASSERT_EQ(res.versions()[1], 501);
ASSERT_EQ(res.table_ids()[2], t1);
ASSERT_EQ(res.partition_ids()[2], t1_p1);
ASSERT_EQ(res.versions()[2], 1001);
}
}
TEST(MetaServiceTest, BeginAndAbortSubTxnTest) {
auto meta_service = get_meta_service();
long db_id = 98762;
int64_t txn_id = -1;
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label("test_label");
txn_info_pb.add_table_ids(1234);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// case: begin 2 sub txn
int64_t sub_txn_id1 = -1;
int64_t sub_txn_id2 = -1;
for (int i = 0; i < 2; i++) {
{
brpc::Controller cntl;
BeginSubTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
req.set_sub_txn_num(i);
req.set_db_id(db_id);
req.set_label("test_label_" + std::to_string(i));
req.mutable_table_ids()->Add(1234);
req.mutable_table_ids()->Add(1235);
if (i == 1) {
req.mutable_table_ids()->Add(1235);
}
BeginSubTxnResponse res;
meta_service->begin_sub_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().table_ids().size(), i == 0 ? 2 : 3);
ASSERT_EQ(res.txn_info().sub_txn_ids().size(), i == 0 ? 1 : 2);
ASSERT_TRUE(res.has_sub_txn_id());
if (i == 0) {
sub_txn_id1 = res.sub_txn_id();
ASSERT_EQ(sub_txn_id1, res.txn_info().sub_txn_ids()[0]);
} else {
sub_txn_id2 = res.sub_txn_id();
ASSERT_EQ(sub_txn_id1, res.txn_info().sub_txn_ids()[0]);
ASSERT_EQ(sub_txn_id2, res.txn_info().sub_txn_ids()[1]);
}
}
// get txn state
{
brpc::Controller cntl;
GetTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
GetTxnResponse res;
meta_service->get_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().table_ids().size(), i == 0 ? 2 : 3);
ASSERT_EQ(res.txn_info().table_ids()[0], 1234);
ASSERT_EQ(res.txn_info().table_ids()[1], 1235);
if (i == 1) {
ASSERT_EQ(res.txn_info().table_ids()[2], 1235);
}
}
}
// case: abort sub txn2 twice
{
for (int i = 0; i < 2; i++) {
brpc::Controller cntl;
AbortSubTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
req.set_sub_txn_id(sub_txn_id2);
req.set_sub_txn_num(2);
req.set_db_id(db_id);
req.mutable_table_ids()->Add(1234);
req.mutable_table_ids()->Add(1235);
AbortSubTxnResponse res;
meta_service->abort_sub_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
// check txn state
ASSERT_EQ(res.txn_info().table_ids().size(), 2);
ASSERT_EQ(res.txn_info().sub_txn_ids().size(), 2);
ASSERT_EQ(sub_txn_id1, res.txn_info().sub_txn_ids()[0]);
ASSERT_EQ(sub_txn_id2, res.txn_info().sub_txn_ids()[1]);
}
// get txn state
{
brpc::Controller cntl;
GetTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
GetTxnResponse res;
meta_service->get_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().table_ids().size(), 2);
ASSERT_EQ(res.txn_info().table_ids()[0], 1234);
ASSERT_EQ(res.txn_info().table_ids()[1], 1235);
}
}
// check label key does not exist
for (int i = 0; i < 2; i++) {
std::string key =
txn_label_key({"test_instance", db_id, "test_label_" + std::to_string(i)});
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
}
// check txn index key exist
for (auto i : {sub_txn_id1, sub_txn_id2}) {
std::string key = txn_index_key({"test_instance", i});
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
}
}
TEST(MetaServiceTest, AbortTxnTest) {
auto meta_service = get_meta_service();
// case: abort txn by txn_id
{
int64_t db_id = 666;
int64_t table_id = 12345;
std::string label = "abort_txn_by_txn_id";
std::string cloud_unique_id = "test_cloud_unique_id";
int64_t tablet_id_base = 1104;
int64_t txn_id = -1;
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id(cloud_unique_id);
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(table_id);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// mock rowset and tablet
for (int i = 0; i < 5; ++i) {
create_tablet(meta_service.get(), 12345, 1235, 1236, tablet_id_base + i);
auto tmp_rowset = create_rowset(txn_id, tablet_id_base + i);
CreateRowsetResponse res;
commit_rowset(meta_service.get(), tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// abort txn by txn_id
{
brpc::Controller cntl;
AbortTxnRequest req;
req.set_cloud_unique_id(cloud_unique_id);
req.set_txn_id(txn_id);
req.set_reason("test");
AbortTxnResponse res;
meta_service->abort_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().status(), TxnStatusPB::TXN_STATUS_ABORTED);
}
}
// case: abort txn by db_id + label
{
int64_t db_id = 66631313131;
int64_t table_id = 12345;
std::string label = "abort_txn_by_db_id_and_label";
std::string cloud_unique_id = "test_cloud_unique_id";
int64_t tablet_id_base = 1104;
int64_t txn_id = -1;
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id(cloud_unique_id);
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(table_id);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// mock rowset and tablet
for (int i = 0; i < 5; ++i) {
create_tablet(meta_service.get(), table_id, 1235, 1236, tablet_id_base + i);
auto tmp_rowset = create_rowset(txn_id, tablet_id_base + i);
CreateRowsetResponse res;
commit_rowset(meta_service.get(), tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// abort txn by db_id and label
{
brpc::Controller cntl;
AbortTxnRequest req;
req.set_cloud_unique_id(cloud_unique_id);
req.set_db_id(db_id);
req.set_label(label);
req.set_reason("test");
AbortTxnResponse res;
meta_service->abort_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().status(), TxnStatusPB::TXN_STATUS_ABORTED);
std::string recycle_txn_key_;
std::string recycle_txn_val;
RecycleTxnKeyInfo recycle_txn_key_info {mock_instance, db_id, txn_id};
recycle_txn_key(recycle_txn_key_info, &recycle_txn_key_);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(recycle_txn_key_, &recycle_txn_val), TxnErrorCode::TXN_OK);
ASSERT_NE(txn_id, -1);
}
}
}
TEST(MetaServiceTest, GetCurrentMaxTxnIdTest) {
auto meta_service = get_meta_service();
const int64_t db_id = 123;
const std::string label = "test_label123";
const std::string cloud_unique_id = "test_cloud_unique_id";
brpc::Controller begin_txn_cntl;
BeginTxnRequest begin_txn_req;
BeginTxnResponse begin_txn_res;
TxnInfoPB txn_info_pb;
begin_txn_req.set_cloud_unique_id(cloud_unique_id);
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(12345);
txn_info_pb.set_timeout_ms(36000);
begin_txn_req.mutable_txn_info()->CopyFrom(txn_info_pb);
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&begin_txn_cntl),
&begin_txn_req, &begin_txn_res, nullptr);
ASSERT_EQ(begin_txn_res.status().code(), MetaServiceCode::OK);
brpc::Controller max_txn_id_cntl;
GetCurrentMaxTxnRequest max_txn_id_req;
GetCurrentMaxTxnResponse max_txn_id_res;
max_txn_id_req.set_cloud_unique_id(cloud_unique_id);
meta_service->get_current_max_txn_id(
reinterpret_cast<::google::protobuf::RpcController*>(&max_txn_id_cntl), &max_txn_id_req,
&max_txn_id_res, nullptr);
ASSERT_EQ(max_txn_id_res.status().code(), MetaServiceCode::OK);
ASSERT_GE(max_txn_id_res.current_max_txn_id(), begin_txn_res.txn_id());
}
TEST(MetaServiceTest, AbortTxnWithCoordinatorTest) {
auto meta_service = get_meta_service();
const int64_t db_id = 666;
const int64_t table_id = 777;
const std::string label = "test_label";
const std::string cloud_unique_id = "test_cloud_unique_id";
const int64_t coordinator_id = 15623;
int64_t cur_time = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
std::string host = "127.0.0.1:15586";
int64_t txn_id = -1;
brpc::Controller begin_txn_cntl;
BeginTxnRequest begin_txn_req;
BeginTxnResponse begin_txn_res;
TxnInfoPB txn_info_pb;
TxnCoordinatorPB coordinator;
begin_txn_req.set_cloud_unique_id(cloud_unique_id);
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(table_id);
txn_info_pb.set_timeout_ms(36000);
coordinator.set_id(coordinator_id);
coordinator.set_ip(host);
coordinator.set_sourcetype(::doris::cloud::TxnSourceTypePB::TXN_SOURCE_TYPE_BE);
coordinator.set_start_time(cur_time);
txn_info_pb.mutable_coordinator()->CopyFrom(coordinator);
begin_txn_req.mutable_txn_info()->CopyFrom(txn_info_pb);
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&begin_txn_cntl),
&begin_txn_req, &begin_txn_res, nullptr);
ASSERT_EQ(begin_txn_res.status().code(), MetaServiceCode::OK);
txn_id = begin_txn_res.txn_id();
ASSERT_GT(txn_id, -1);
brpc::Controller abort_txn_cntl;
AbortTxnWithCoordinatorRequest abort_txn_req;
AbortTxnWithCoordinatorResponse abort_txn_resp;
abort_txn_req.set_id(coordinator_id);
abort_txn_req.set_ip(host);
abort_txn_req.set_start_time(cur_time + 3600);
// first time to check txn conflict
meta_service->abort_txn_with_coordinator(
reinterpret_cast<::google::protobuf::RpcController*>(&begin_txn_cntl), &abort_txn_req,
&abort_txn_resp, nullptr);
ASSERT_EQ(abort_txn_resp.status().code(), MetaServiceCode::OK);
brpc::Controller abort_txn_conflict_cntl;
CheckTxnConflictRequest check_txn_conflict_req;
CheckTxnConflictResponse check_txn_conflict_res;
check_txn_conflict_req.set_cloud_unique_id(cloud_unique_id);
check_txn_conflict_req.set_db_id(db_id);
check_txn_conflict_req.set_end_txn_id(txn_id + 1);
check_txn_conflict_req.add_table_ids(table_id);
// first time to check txn conflict
meta_service->check_txn_conflict(
reinterpret_cast<::google::protobuf::RpcController*>(&begin_txn_cntl),
&check_txn_conflict_req, &check_txn_conflict_res, nullptr);
ASSERT_EQ(check_txn_conflict_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(check_txn_conflict_res.finished(), true);
ASSERT_EQ(check_txn_conflict_res.conflict_txns_size(), 0);
}
TEST(MetaServiceTest, CheckTxnConflictTest) {
auto meta_service = get_meta_service();
const int64_t db_id = 666;
const int64_t table_id = 777;
const std::string label = "test_label";
const std::string cloud_unique_id = "test_cloud_unique_id";
int64_t txn_id = -1;
brpc::Controller begin_txn_cntl;
BeginTxnRequest begin_txn_req;
BeginTxnResponse begin_txn_res;
TxnInfoPB txn_info_pb;
begin_txn_req.set_cloud_unique_id(cloud_unique_id);
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(table_id);
txn_info_pb.set_timeout_ms(36000);
begin_txn_req.mutable_txn_info()->CopyFrom(txn_info_pb);
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&begin_txn_cntl),
&begin_txn_req, &begin_txn_res, nullptr);
ASSERT_EQ(begin_txn_res.status().code(), MetaServiceCode::OK);
txn_id = begin_txn_res.txn_id();
ASSERT_GT(txn_id, -1);
brpc::Controller check_txn_conflict_cntl;
CheckTxnConflictRequest check_txn_conflict_req;
CheckTxnConflictResponse check_txn_conflict_res;
check_txn_conflict_req.set_cloud_unique_id(cloud_unique_id);
check_txn_conflict_req.set_db_id(db_id);
check_txn_conflict_req.set_end_txn_id(txn_id + 1);
check_txn_conflict_req.add_table_ids(table_id);
// first time to check txn conflict
meta_service->check_txn_conflict(
reinterpret_cast<::google::protobuf::RpcController*>(&begin_txn_cntl),
&check_txn_conflict_req, &check_txn_conflict_res, nullptr);
ASSERT_EQ(check_txn_conflict_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(check_txn_conflict_res.finished(), false);
ASSERT_EQ(check_txn_conflict_res.conflict_txns_size(), 1);
check_txn_conflict_res.clear_conflict_txns();
// mock rowset and tablet
int64_t tablet_id_base = 123456;
for (int i = 0; i < 5; ++i) {
create_tablet(meta_service.get(), table_id, 1235, 1236, tablet_id_base + i);
auto tmp_rowset = create_rowset(txn_id, tablet_id_base + i);
CreateRowsetResponse res;
commit_rowset(meta_service.get(), tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
brpc::Controller commit_txn_cntl;
CommitTxnRequest commit_txn_req;
commit_txn_req.set_cloud_unique_id(cloud_unique_id);
commit_txn_req.set_db_id(db_id);
commit_txn_req.set_txn_id(txn_id);
CommitTxnResponse commit_txn_res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&commit_txn_cntl),
&commit_txn_req, &commit_txn_res, nullptr);
ASSERT_EQ(commit_txn_res.status().code(), MetaServiceCode::OK);
// second time to check txn conflict
meta_service->check_txn_conflict(
reinterpret_cast<::google::protobuf::RpcController*>(&check_txn_conflict_cntl),
&check_txn_conflict_req, &check_txn_conflict_res, nullptr);
ASSERT_EQ(check_txn_conflict_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(check_txn_conflict_res.finished(), true);
ASSERT_EQ(check_txn_conflict_res.conflict_txns_size(), 0);
{
std::string running_key = txn_running_key({mock_instance, db_id, txn_id});
std::string running_value;
std::unique_ptr<Transaction> txn;
TxnErrorCode err = meta_service->txn_kv()->create_txn(&txn);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(running_key, &running_value), TxnErrorCode::TXN_KEY_NOT_FOUND);
}
}
TEST(MetaServiceTest, CheckNotTimeoutTxnConflictTest) {
auto meta_service = get_meta_service();
const int64_t db_id = 666;
const int64_t table_id = 777;
const std::string label = "test_label";
const std::string cloud_unique_id = "test_cloud_unique_id";
int64_t txn_id = -1;
brpc::Controller begin_txn_cntl;
BeginTxnRequest begin_txn_req;
BeginTxnResponse begin_txn_res;
TxnInfoPB txn_info_pb;
begin_txn_req.set_cloud_unique_id(cloud_unique_id);
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(table_id);
txn_info_pb.set_timeout_ms(3);
begin_txn_req.mutable_txn_info()->CopyFrom(txn_info_pb);
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&begin_txn_cntl),
&begin_txn_req, &begin_txn_res, nullptr);
ASSERT_EQ(begin_txn_res.status().code(), MetaServiceCode::OK);
txn_id = begin_txn_res.txn_id();
ASSERT_GT(txn_id, -1);
brpc::Controller check_txn_conflict_cntl;
CheckTxnConflictRequest check_txn_conflict_req;
CheckTxnConflictResponse check_txn_conflict_res;
check_txn_conflict_req.set_cloud_unique_id(cloud_unique_id);
check_txn_conflict_req.set_db_id(db_id);
check_txn_conflict_req.set_end_txn_id(txn_id + 1);
check_txn_conflict_req.add_table_ids(table_id);
// wait txn timeout
sleep(5);
// first time to check txn conflict
meta_service->check_txn_conflict(
reinterpret_cast<::google::protobuf::RpcController*>(&begin_txn_cntl),
&check_txn_conflict_req, &check_txn_conflict_res, nullptr);
ASSERT_EQ(check_txn_conflict_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(check_txn_conflict_res.finished(), true);
}
TEST(MetaServiceTest, CheckTxnConflictWithAbortLabelTest) {
int ret = 0;
auto txn_kv = std::dynamic_pointer_cast<TxnKv>(std::make_shared<MemTxnKv>());
if (txn_kv != nullptr) {
ret = txn_kv->init();
[&] { ASSERT_EQ(ret, 0); }();
}
[&] { ASSERT_NE(txn_kv.get(), nullptr); }();
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->remove("\x00", "\xfe"); // This is dangerous if the fdb is not correctly set
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
}
auto rs = std::make_shared<MockResourceManager>(txn_kv);
auto rl = std::make_shared<RateLimiter>();
auto snapshot = std::make_shared<SnapshotManager>(txn_kv);
auto meta_service = std::make_unique<MetaServiceProxy>(
std::make_unique<MetaServiceImpl>(txn_kv, rs, rl, snapshot));
const int64_t db_id = 666;
const int64_t table_id = 777;
const std::string label = "test_label";
const std::string cloud_unique_id = "test_cloud_unique_id";
int64_t txn_id = -1;
brpc::Controller begin_txn_cntl;
BeginTxnRequest begin_txn_req;
BeginTxnResponse begin_txn_res;
TxnInfoPB txn_info_pb;
begin_txn_req.set_cloud_unique_id(cloud_unique_id);
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(table_id);
txn_info_pb.set_timeout_ms(36000);
begin_txn_req.mutable_txn_info()->CopyFrom(txn_info_pb);
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&begin_txn_cntl),
&begin_txn_req, &begin_txn_res, nullptr);
ASSERT_EQ(begin_txn_res.status().code(), MetaServiceCode::OK);
txn_id = begin_txn_res.txn_id();
ASSERT_GT(txn_id, -1);
brpc::Controller check_txn_conflict_cntl;
CheckTxnConflictRequest check_txn_conflict_req;
CheckTxnConflictResponse check_txn_conflict_res;
check_txn_conflict_req.set_cloud_unique_id(cloud_unique_id);
check_txn_conflict_req.set_db_id(db_id);
check_txn_conflict_req.set_end_txn_id(txn_id + 1);
check_txn_conflict_req.add_table_ids(table_id);
// first time to check txn conflict
meta_service->check_txn_conflict(
reinterpret_cast<::google::protobuf::RpcController*>(&begin_txn_cntl),
&check_txn_conflict_req, &check_txn_conflict_res, nullptr);
ASSERT_EQ(check_txn_conflict_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(check_txn_conflict_res.finished(), false);
std::string running_key;
std::string running_val;
txn_running_key({mock_instance, db_id, txn_id}, &running_key);
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(running_key, &running_val), TxnErrorCode::TXN_OK);
}
brpc::Controller abort_txn_cntl;
AbortTxnRequest abort_txn_req;
abort_txn_req.set_cloud_unique_id(cloud_unique_id);
abort_txn_req.set_db_id(db_id);
abort_txn_req.set_label(label);
AbortTxnResponse abort_txn_res;
meta_service->abort_txn(reinterpret_cast<::google::protobuf::RpcController*>(&abort_txn_cntl),
&abort_txn_req, &abort_txn_res, nullptr);
ASSERT_EQ(abort_txn_res.status().code(), MetaServiceCode::OK);
// second time to check txn conflict
meta_service->check_txn_conflict(
reinterpret_cast<::google::protobuf::RpcController*>(&check_txn_conflict_cntl),
&check_txn_conflict_req, &check_txn_conflict_res, nullptr);
ASSERT_EQ(check_txn_conflict_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(check_txn_conflict_res.finished(), true);
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(running_key, &running_val), TxnErrorCode::TXN_KEY_NOT_FOUND);
}
}
TEST(MetaServiceTest, CleanTxnLabelTest) {
int ret = 0;
auto txn_kv = std::dynamic_pointer_cast<TxnKv>(std::make_shared<MemTxnKv>());
if (txn_kv != nullptr) {
ret = txn_kv->init();
[&] { ASSERT_EQ(ret, 0); }();
}
[&] { ASSERT_NE(txn_kv.get(), nullptr); }();
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->remove("\x00", "\xfe"); // This is dangerous if the fdb is not correctly set
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
}
auto rs = std::make_shared<MockResourceManager>(txn_kv);
auto rl = std::make_shared<RateLimiter>();
auto snapshot = std::make_shared<SnapshotManager>(txn_kv);
auto meta_service = std::make_unique<MetaServiceProxy>(
std::make_unique<MetaServiceImpl>(txn_kv, rs, rl, snapshot));
// clean txn label by db_id and label
{
int64_t txn_id = -1;
int64_t db_id = 1987211;
const std::string& label = "test_clean_label";
{
brpc::Controller cntl;
CleanTxnLabelRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.add_labels(label);
CleanTxnLabelResponse res;
meta_service->clean_txn_label(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(1234);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
const std::string info_key = txn_info_key({mock_instance, db_id, txn_id});
std::string info_val;
const std::string label_key = txn_label_key({mock_instance, db_id, label});
std::string label_val;
const std::string index_key = txn_index_key({mock_instance, txn_id});
std::string index_val;
const std::string running_key = txn_running_key({mock_instance, db_id, txn_id});
std::string running_val;
const std::string recycle_key = recycle_txn_key({mock_instance, db_id, txn_id});
std::string recycle_val;
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
TxnErrorCode err = txn->get(info_key, &info_val);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
err = txn->get(label_key, &label_val);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
err = txn->get(index_key, &index_val);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
err = txn->get(running_key, &running_val);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
err = txn->get(recycle_key, &recycle_val);
ASSERT_EQ(err, TxnErrorCode::TXN_KEY_NOT_FOUND);
}
// mock rowset and tablet
int64_t tablet_id_base = 110313131;
for (int i = 0; i < 2; ++i) {
create_tablet(meta_service.get(), 1234, 1235, 1236, tablet_id_base + i);
auto tmp_rowset = create_rowset(txn_id, tablet_id_base + i);
CreateRowsetResponse res;
commit_rowset(meta_service.get(), tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// commit txn
{
brpc::Controller cntl;
CommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(txn_id);
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(1234);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::TXN_LABEL_ALREADY_USED);
}
// clean txn label
{
brpc::Controller cntl;
CleanTxnLabelRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
CleanTxnLabelResponse res;
meta_service->clean_txn_label(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// clean txn label
{
brpc::Controller cntl;
CleanTxnLabelRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.add_labels(label);
CleanTxnLabelResponse res;
meta_service->clean_txn_label(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
TxnErrorCode err = txn->get(info_key, &info_val);
ASSERT_EQ(err, TxnErrorCode::TXN_KEY_NOT_FOUND);
err = txn->get(label_key, &label_val);
ASSERT_EQ(err, TxnErrorCode::TXN_KEY_NOT_FOUND);
err = txn->get(index_key, &index_val);
ASSERT_EQ(err, TxnErrorCode::TXN_KEY_NOT_FOUND);
err = txn->get(running_key, &running_val);
ASSERT_EQ(err, TxnErrorCode::TXN_KEY_NOT_FOUND);
err = txn->get(recycle_key, &recycle_val);
ASSERT_EQ(err, TxnErrorCode::TXN_KEY_NOT_FOUND);
}
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(1234);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// abort txn
{
brpc::Controller cntl;
AbortTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
ASSERT_GT(txn_id, 0);
req.set_txn_id(txn_id);
req.set_reason("test");
AbortTxnResponse res;
meta_service->abort_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().status(), TxnStatusPB::TXN_STATUS_ABORTED);
}
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(1234);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// clean txn label
{
brpc::Controller cntl;
CleanTxnLabelRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.add_labels(label);
CleanTxnLabelResponse res;
meta_service->clean_txn_label(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// abort txn
{
brpc::Controller cntl;
AbortTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
ASSERT_GT(txn_id, 0);
req.set_txn_id(txn_id);
req.set_reason("test");
AbortTxnResponse res;
meta_service->abort_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.txn_info().status(), TxnStatusPB::TXN_STATUS_ABORTED);
}
// clean txn label
{
brpc::Controller cntl;
CleanTxnLabelRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.add_labels(label);
CleanTxnLabelResponse res;
meta_service->clean_txn_label(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
{
const std::string info_key = txn_info_key({mock_instance, db_id, txn_id});
std::string info_val;
const std::string label_key = txn_label_key({mock_instance, db_id, label});
std::string label_val;
const std::string index_key = txn_index_key({mock_instance, txn_id});
std::string index_val;
const std::string running_key = txn_running_key({mock_instance, db_id, txn_id});
std::string running_val;
const std::string recycle_key = recycle_txn_key({mock_instance, db_id, txn_id});
std::string recycle_val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
TxnErrorCode ret = txn->get(info_key, &info_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_KEY_NOT_FOUND);
ret = txn->get(label_key, &label_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_KEY_NOT_FOUND);
ret = txn->get(index_key, &index_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_KEY_NOT_FOUND);
ret = txn->get(running_key, &running_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_KEY_NOT_FOUND);
ret = txn->get(recycle_key, &recycle_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_KEY_NOT_FOUND);
}
}
// clean txn label only by db_id
{
int64_t db_id = 1987211123;
const std::string& label = "test_clean_label";
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.add_table_ids(1234);
txn_info_pb.set_timeout_ms(36000);
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
//clean not exist label
{
brpc::Controller cntl;
CleanTxnLabelRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
CleanTxnLabelResponse res;
meta_service->clean_txn_label(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// inject internal_clean_label err = TXN_CONFLICT
{
auto sp = SyncPoint::get_instance();
sp->set_call_back("internal_clean_label:err", [&](auto&& args) {
auto* err = try_any_cast<TxnErrorCode*>(args[0]);
*err = TxnErrorCode::TXN_CONFLICT;
});
sp->enable_processing();
int64_t txn_id = -1;
for (int i = 100; i < 101; i++) {
{
std::stringstream label_ss;
label_ss << label << i;
brpc::Controller cntl;
txn_info_pb.set_label(label_ss.str());
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
{
// mock rowset and tablet
int64_t tablet_id_base = 110313131;
for (int i = 0; i < 1; ++i) {
create_tablet(meta_service.get(), 1234, 1235, 1236, tablet_id_base + i);
auto tmp_rowset = create_rowset(txn_id, tablet_id_base + i);
CreateRowsetResponse res;
commit_rowset(meta_service.get(), tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
}
// commit txn
{
brpc::Controller cntl;
CommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(txn_id);
CommitTxnResponse res;
meta_service->commit_txn(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
}
{
brpc::Controller cntl;
CleanTxnLabelRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
CleanTxnLabelResponse res;
meta_service->clean_txn_label(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::KV_TXN_CONFLICT);
}
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
// create 12 committed txns and clean label by id
{
auto sp = SyncPoint::get_instance();
sp->set_call_back("clean_txn_label:limit", [](auto&& args) {
int* limit = try_any_cast<int*>(args[0]);
*limit = 5;
});
sp->enable_processing();
int64_t txn_id = -1;
for (int i = 0; i < 12; i++) {
{
std::stringstream label_ss;
label_ss << label << i;
brpc::Controller cntl;
txn_info_pb.set_label(label_ss.str());
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
{
// mock rowset and tablet
int64_t tablet_id_base = 110313131;
for (int i = 0; i < 1; ++i) {
create_tablet(meta_service.get(), 1234, 1235, 1236, tablet_id_base + i);
auto tmp_rowset = create_rowset(txn_id, tablet_id_base + i);
CreateRowsetResponse res;
commit_rowset(meta_service.get(), tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
}
// commit txn
{
brpc::Controller cntl;
CommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(txn_id);
CommitTxnResponse res;
meta_service->commit_txn(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
}
{
brpc::Controller cntl;
CleanTxnLabelRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
CleanTxnLabelResponse res;
meta_service->clean_txn_label(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res,
nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
}
}
TEST(MetaServiceTest, GetTxnTest) {
int ret = 0;
auto txn_kv = std::dynamic_pointer_cast<TxnKv>(std::make_shared<MemTxnKv>());
if (txn_kv != nullptr) {
ret = txn_kv->init();
[&] { ASSERT_EQ(ret, 0); }();
}
[&] { ASSERT_NE(txn_kv.get(), nullptr); }();
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->remove("\x00", "\xfe"); // This is dangerous if the fdb is not correctly set
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
}
auto rs = std::make_shared<MockResourceManager>(txn_kv);
auto rl = std::make_shared<RateLimiter>();
auto snapshot = std::make_shared<SnapshotManager>(txn_kv);
auto meta_service = std::make_unique<MetaServiceProxy>(
std::make_unique<MetaServiceImpl>(txn_kv, rs, rl, snapshot));
{
int64_t txn_id = -1;
int64_t db_id = 34521431231;
const std::string& label = "test_get_txn";
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label(label);
txn_info_pb.add_table_ids(1234);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
{
brpc::Controller cntl;
GetTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(-1);
GetTxnResponse res;
meta_service->get_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
{
brpc::Controller cntl;
GetTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(txn_id);
GetTxnResponse res;
meta_service->get_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
{
brpc::Controller cntl;
GetTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_txn_id(txn_id);
GetTxnResponse res;
meta_service->get_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
}
}
//
TEST(MetaServiceTest, CopyJobTest) {
auto meta_service = get_meta_service();
brpc::Controller cntl;
auto cloud_unique_id = "test_cloud_unique_id";
auto stage_id = "test_stage_id";
int64_t table_id = 100;
std::string instance_id = "copy_job_test_instance_id";
[[maybe_unused]] auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->enable_processing();
// generate a begin copy request
BeginCopyRequest begin_copy_request;
begin_copy_request.set_cloud_unique_id(cloud_unique_id);
begin_copy_request.set_stage_id(stage_id);
begin_copy_request.set_stage_type(StagePB::EXTERNAL);
begin_copy_request.set_table_id(table_id);
begin_copy_request.set_copy_id("test_copy_id");
begin_copy_request.set_group_id(0);
begin_copy_request.set_start_time_ms(200);
begin_copy_request.set_timeout_time_ms(300);
for (int i = 0; i < 20; ++i) {
ObjectFilePB object_file_pb;
object_file_pb.set_relative_path("obj_" + std::to_string(i));
object_file_pb.set_etag("obj_" + std::to_string(i) + "_etag");
begin_copy_request.add_object_files()->CopyFrom(object_file_pb);
}
// generate a finish copy request
FinishCopyRequest finish_copy_request;
finish_copy_request.set_cloud_unique_id(cloud_unique_id);
finish_copy_request.set_stage_id(stage_id);
finish_copy_request.set_stage_type(StagePB::EXTERNAL);
finish_copy_request.set_table_id(table_id);
finish_copy_request.set_copy_id("test_copy_id");
finish_copy_request.set_group_id(0);
finish_copy_request.set_action(FinishCopyRequest::COMMIT);
// generate a get copy files request
GetCopyFilesRequest get_copy_file_req;
get_copy_file_req.set_cloud_unique_id(cloud_unique_id);
get_copy_file_req.set_stage_id(stage_id);
get_copy_file_req.set_table_id(table_id);
// generate a get copy job request
GetCopyJobRequest get_copy_job_request;
get_copy_job_request.set_cloud_unique_id(cloud_unique_id);
get_copy_job_request.set_stage_id(stage_id);
get_copy_job_request.set_table_id(table_id);
get_copy_job_request.set_copy_id("test_copy_id");
get_copy_job_request.set_group_id(0);
// get copy job
{
GetCopyJobResponse res;
meta_service->get_copy_job(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_copy_job_request, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.has_copy_job(), false);
}
// begin copy
{
BeginCopyResponse res;
meta_service->begin_copy(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&begin_copy_request, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.filtered_object_files_size(), 20);
}
// get copy files
{
GetCopyFilesResponse res;
meta_service->get_copy_files(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_copy_file_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.object_files_size(), 20);
}
// get copy job
{
GetCopyJobResponse res;
meta_service->get_copy_job(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_copy_job_request, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.copy_job().object_files().size(), 20);
}
// begin copy with duplicate files
{
begin_copy_request.set_copy_id("test_copy_id_1");
begin_copy_request.clear_object_files();
for (int i = 15; i < 30; ++i) {
ObjectFilePB object_file_pb;
object_file_pb.set_relative_path("obj_" + std::to_string(i));
object_file_pb.set_etag("obj_" + std::to_string(i) + "_etag");
begin_copy_request.add_object_files()->CopyFrom(object_file_pb);
}
BeginCopyResponse res;
meta_service->begin_copy(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&begin_copy_request, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.filtered_object_files_size(), 10);
}
// get copy files
{
GetCopyFilesResponse res;
meta_service->get_copy_files(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_copy_file_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.object_files_size(), 30);
}
// finish the first copy job
{
FinishCopyResponse res;
meta_service->finish_copy(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&finish_copy_request, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// get copy files
{
GetCopyFilesResponse res;
meta_service->get_copy_files(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_copy_file_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.object_files_size(), 30);
}
// abort the second copy job
{
finish_copy_request.set_copy_id("test_copy_id_1");
finish_copy_request.set_action(FinishCopyRequest::ABORT);
FinishCopyResponse res;
meta_service->finish_copy(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&finish_copy_request, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// get copy files
{
GetCopyFilesResponse res;
meta_service->get_copy_files(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_copy_file_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.object_files_size(), 20);
}
{
// begin a copy job whose files are all loaded, the copy job key should not be created
begin_copy_request.set_copy_id("tmp_id");
begin_copy_request.clear_object_files();
for (int i = 0; i < 20; ++i) {
ObjectFilePB object_file_pb;
object_file_pb.set_relative_path("obj_" + std::to_string(i));
object_file_pb.set_etag("obj_" + std::to_string(i) + "_etag");
begin_copy_request.add_object_files()->CopyFrom(object_file_pb);
}
BeginCopyResponse res;
meta_service->begin_copy(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&begin_copy_request, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.filtered_object_files_size(), 0);
// get copy job
get_copy_job_request.set_copy_id("tmp_id");
GetCopyJobResponse res2;
meta_service->get_copy_job(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_copy_job_request, &res2, nullptr);
ASSERT_EQ(res2.status().code(), MetaServiceCode::OK);
ASSERT_FALSE(res2.has_copy_job());
}
// scan fdb
{
std::unique_ptr<Transaction> txn;
std::string get_val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
// 20 copy files
{
CopyFileKeyInfo key_info0 {instance_id, stage_id, table_id, "", ""};
CopyFileKeyInfo key_info1 {instance_id, stage_id, table_id + 1, "", ""};
std::string key0;
std::string key1;
copy_file_key(key_info0, &key0);
copy_file_key(key_info1, &key1);
std::unique_ptr<RangeGetIterator> it;
ASSERT_EQ(txn->get(key0, key1, &it), TxnErrorCode::TXN_OK);
int file_cnt = 0;
do {
ASSERT_EQ(txn->get(key0, key1, &it), TxnErrorCode::TXN_OK);
while (it->has_next()) {
auto [k, v] = it->next();
CopyFilePB copy_file;
ASSERT_TRUE(copy_file.ParseFromArray(v.data(), v.size()));
ASSERT_EQ(copy_file.copy_id(), "test_copy_id");
++file_cnt;
if (!it->has_next()) {
key0 = k;
}
}
key0.push_back('\x00');
} while (it->more());
ASSERT_EQ(file_cnt, 20);
}
// 1 copy job with finish status
{
CopyJobKeyInfo key_info0 {instance_id, stage_id, table_id, "", 0};
CopyJobKeyInfo key_info1 {instance_id, stage_id, table_id + 1, "", 0};
std::string key0;
std::string key1;
copy_job_key(key_info0, &key0);
copy_job_key(key_info1, &key1);
std::unique_ptr<RangeGetIterator> it;
int job_cnt = 0;
do {
ASSERT_EQ(txn->get(key0, key1, &it), TxnErrorCode::TXN_OK);
while (it->has_next()) {
auto [k, v] = it->next();
CopyJobPB copy_job;
ASSERT_EQ(copy_job.ParseFromArray(v.data(), v.size()), true);
ASSERT_EQ(copy_job.object_files_size(), 20);
ASSERT_EQ(copy_job.job_status(), CopyJobPB::FINISH);
++job_cnt;
if (!it->has_next()) {
key0 = k;
}
}
key0.push_back('\x00');
} while (it->more());
ASSERT_EQ(job_cnt, 1);
}
}
}
TEST(MetaServiceTest, FilterCopyFilesTest) {
auto meta_service = get_meta_service();
brpc::Controller cntl;
auto cloud_unique_id = "test_cloud_unique_id";
std::string instance_id = "stage_test_instance_id";
auto stage_id = "test_stage_id";
int64_t table_id = 100;
[[maybe_unused]] auto sp = SyncPoint::get_instance();
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->set_call_back("decrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* key = try_any_cast<std::string*>(args[0]);
*key = "test";
auto* ret = try_any_cast<int*>(args[1]);
*ret = 0;
});
sp->enable_processing();
FilterCopyFilesRequest request;
request.set_cloud_unique_id(cloud_unique_id);
request.set_stage_id(stage_id);
request.set_table_id(table_id);
for (int i = 0; i < 10; ++i) {
ObjectFilePB object_file;
object_file.set_relative_path("file" + std::to_string(i));
object_file.set_etag("etag" + std::to_string(i));
request.add_object_files()->CopyFrom(object_file);
}
// all files are not loaded
{
FilterCopyFilesResponse res;
meta_service->filter_copy_files(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&request, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.object_files().size(), 10);
}
// some files are loaded
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
for (int i = 0; i < 4; ++i) {
CopyFileKeyInfo key_info {instance_id, stage_id, table_id, "file" + std::to_string(i),
"etag" + std::to_string(i)};
std::string key;
copy_file_key(key_info, &key);
CopyFilePB copy_file;
copy_file.set_copy_id("test_copy_id");
std::string val;
copy_file.SerializeToString(&val);
txn->put(key, val);
}
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
FilterCopyFilesResponse res;
meta_service->filter_copy_files(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&request, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.object_files().size(), 6);
ASSERT_EQ(res.object_files().at(0).relative_path(), "file4");
}
// all files are loaded
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
for (int i = 4; i < 10; ++i) {
CopyFileKeyInfo key_info {instance_id, stage_id, table_id, "file" + std::to_string(i),
"etag" + std::to_string(i)};
std::string key;
copy_file_key(key_info, &key);
CopyFilePB copy_file;
copy_file.set_copy_id("test_copy_id");
std::string val;
copy_file.SerializeToString(&val);
txn->put(key, val);
}
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
FilterCopyFilesResponse res;
meta_service->filter_copy_files(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&request, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.object_files().size(), 0);
}
}
extern std::vector<std::pair<int64_t, int64_t>> calc_sync_versions(
int64_t req_bc_cnt, int64_t bc_cnt, int64_t req_cc_cnt, int64_t cc_cnt, int64_t req_cp,
int64_t cp, int64_t req_fc_cnt, int64_t fc_cnt, int64_t req_start, int64_t req_end);
TEST(MetaServiceTest, CalcSyncVersionsTest) {
using Versions = std::vector<std::pair<int64_t, int64_t>>;
// * no compaction happened
// req_cc_cnt == ms_cc_cnt && req_bc_cnt == ms_bc_cnt && req_cp == ms_cp
// BE [=][=][=][=][=====][=][=]<.......>
// ^~~~~ req_cp
// BE [=][=][=][=][=====][=][=][=][=][=][=]
// ^~~~~ ms_cp
// ^_____^ versions_return: [req_start, req_end]
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 0};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 1};
auto [req_cp, cp] = std::tuple {5, 5};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{8, 12}}));
}
// * only one CC happened and CP changed
// req_cc_cnt == ms_cc_cnt - 1 && req_bc_cnt == ms_bc_cnt && req_cp < ms_cp
// BE [=][=][=][=][=====][=][=]<.......>
// ^~~~~ req_cp
// MS [=][=][=][=][xxxxxxxxxxxxxx][=======][=][=]
// ^~~~~~~ ms_cp
// ^__________________^ versions_return: [req_cp, ms_cp - 1] v [req_start, req_end]
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 0};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 2};
auto [req_cp, cp] = std::tuple {5, 10};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{5, 12}})); // [5, 9] v [8, 12]
}
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 0};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 2};
auto [req_cp, cp] = std::tuple {5, 15};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{5, 14}})); // [5, 14] v [8, 12]
}
// * only one CC happened and CP remain unchanged
// req_cc_cnt == ms_cc_cnt - 1 && req_bc_cnt == ms_bc_cnt && req_cp == ms_cp
// BE [=][=][=][=][=====][=][=]<.......>
// ^~~~~ req_cp
// MS [=][=][=][=][xxxxxxxxxxxxxx][=][=][=][=][=]
// ^~~~~~~~~~~~~~ ms_cp
// ^__________________^ versions_return: [req_cp, max] v [req_start, req_end]
//
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 0};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 2};
auto [req_cp, cp] = std::tuple {5, 5};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{5, INT64_MAX - 1}})); // [5, max] v [8, 12]
}
// * more than one CC happened and CP remain unchanged
// req_cc_cnt < ms_cc_cnt - 1 && req_bc_cnt == ms_bc_cnt && req_cp == ms_cp
// BE [=][=][=][=][=====][=][=]<.......>
// ^~~~~ req_cp
// MS [=][=][=][=][xxxxxxxxxxxxxx][xxxxxxx][=][=]
// ^~~~~~~~~~~~~~ ms_cp
// ^_____________________^ versions_return: [req_cp, max] v [req_start, req_end]
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 0};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 3};
auto [req_cp, cp] = std::tuple {5, 5};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{5, INT64_MAX - 1}})); // [5, max] v [8, 12]
}
// * more than one CC happened and CP changed
// BE [=][=][=][=][=====][=][=]
// ^~~~~ req_cp
// MS [=][=][=][=][xxxxxxxxxxxxxx][xxxxxxx][=][=]
// ^~~~~~~ ms_cp
// ^_____________________^ related_versions: [req_cp, max] v [req_start, req_end]
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 0};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 3};
auto [req_cp, cp] = std::tuple {5, 15};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{5, INT64_MAX - 1}})); // [5, max] v [8, 12]
}
// * for any BC happended
// req_bc_cnt < ms_bc_cnt
// BE [=][=][=][=][=====][=][=]<.......>
// ^~~~~ req_cp
// MS [xxxxxxxxxx][xxxxxxxxxxxxxx][=======][=][=]
// ^~~~~~~ ms_cp
// ^_________________________^ versions_return: [0, ms_cp - 1] v versions_return_in_above_case
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 1};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 1};
auto [req_cp, cp] = std::tuple {5, 5};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{0, 4}, {8, 12}}));
}
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 1};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 1};
auto [req_cp, cp] = std::tuple {8, 8};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{0, 12}})); // [0, 7] v [8, 12]
}
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 1};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 2};
auto [req_cp, cp] = std::tuple {5, 10};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{0, 12}})); // [0, 4] v [5, 9] v [8, 12]
}
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 1};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 2};
auto [req_cp, cp] = std::tuple {5, 15};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{0, 14}})); // [0, 4] v [5, 14] v [8, 12]
}
{
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 1};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 2};
auto [req_cp, cp] = std::tuple {5, 5};
auto [req_start, req_end] = std::tuple {8, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
// [0, 4] v [5, max] v [8, 12]
ASSERT_EQ(versions, (Versions {{0, INT64_MAX - 1}}));
}
{
// when there exists full compaction, we can't optimize by "* only one CC happened and CP changed"
// * one CC happened and CP changed, and full compaction happened
// BE [=][=][=][=][=][=][=][=][=][=]
// ^~~~~ req_cp
// MS [xxxxxxxxxxxxxx][xxxxxxxxxxxxxx][=======][=][=]
// ^~~~~~~ ms_cp
// ^___________________________^ related_versions: [req_cp, max]
//
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 1};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 2};
auto [req_cp, cp] = std::tuple {4, 7};
auto [req_start, req_end] = std::tuple {9, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 1,
req_start, req_end);
ASSERT_EQ(versions, (Versions {{0, INT64_MAX - 1}}));
}
{
// abnormal case:
auto [req_bc_cnt, bc_cnt] = std::tuple {0, 1};
auto [req_cc_cnt, cc_cnt] = std::tuple {1, 2};
auto [req_cp, cp] = std::tuple {4, 7};
auto [req_start, req_end] = std::tuple {9, 12};
auto versions = calc_sync_versions(req_bc_cnt, bc_cnt, req_cc_cnt, cc_cnt, req_cp, cp, 0, 0,
req_start, req_end);
// when not considering full compaction, the returned versions is wrong becasue rowsets in [7-8] are missed
ASSERT_EQ(versions, (Versions {{0, 6}, {9, 12}}));
}
}
TEST(MetaServiceTest, StageTest) {
auto meta_service = get_meta_service();
brpc::Controller cntl;
auto cloud_unique_id = "test_cloud_unique_id";
std::string instance_id = "stage_test_instance_id";
[[maybe_unused]] auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->set_call_back("decrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* key = try_any_cast<std::string*>(args[0]);
*key = "test";
auto* ret = try_any_cast<int*>(args[1]);
*ret = 0;
});
sp->enable_processing();
ObjectStoreInfoPB obj;
obj.set_ak("123");
obj.set_sk("321");
obj.set_bucket("456");
obj.set_prefix("654");
obj.set_endpoint("789");
obj.set_region("987");
obj.set_external_endpoint("888");
obj.set_provider(ObjectStoreInfoPB::BOS);
RamUserPB ram_user;
ram_user.set_user_id("test_user_id");
ram_user.set_ak("test_ak");
ram_user.set_sk("test_sk");
EncryptionInfoPB encry_info;
encry_info.set_encryption_method("encry_method_test");
encry_info.set_key_id(1111);
ram_user.mutable_encryption_info()->CopyFrom(encry_info);
// create instance
{
CreateInstanceRequest req;
req.set_instance_id(instance_id);
req.set_user_id("test_user");
req.set_name("test_name");
req.mutable_ram_user()->CopyFrom(ram_user);
req.mutable_obj_info()->CopyFrom(obj);
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// test create and get internal stage
{
// get a non-existent internal stage
GetStageRequest get_stage_req;
GetStageResponse res;
// no cloud_unique_id
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
get_stage_req.set_cloud_unique_id(cloud_unique_id);
// no instance_id
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = "";
ret->second = true;
});
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
sp->clear_call_back("get_instance_id");
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
// no stage type
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
get_stage_req.set_type(StagePB::INTERNAL);
// no internal stage user name
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
get_stage_req.set_mysql_user_name("root");
// no internal stage user id
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
get_stage_req.set_mysql_user_id("root_id");
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::STAGE_NOT_FOUND);
// create an internal stage
CreateStageRequest create_stage_request;
StagePB stage;
stage.set_type(StagePB::INTERNAL);
stage.add_mysql_user_name("root");
stage.add_mysql_user_id("root_id");
stage.set_stage_id("internal_stage_id");
create_stage_request.set_cloud_unique_id(cloud_unique_id);
create_stage_request.mutable_stage()->CopyFrom(stage);
CreateStageResponse create_stage_response;
meta_service->create_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&create_stage_request, &create_stage_response, nullptr);
ASSERT_EQ(create_stage_response.status().code(), MetaServiceCode::OK);
// get existent internal stage
GetStageResponse res2;
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res2, nullptr);
ASSERT_EQ(res2.status().code(), MetaServiceCode::OK);
ASSERT_EQ(1, res2.stage().size());
// can't find user id's stage
GetStageResponse res3;
get_stage_req.set_mysql_user_id("not_root_id_exist");
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res3, nullptr);
ASSERT_EQ(res3.status().code(), MetaServiceCode::STATE_ALREADY_EXISTED_FOR_USER);
ASSERT_EQ(1, res3.stage().size());
// drop internal stage
DropStageRequest drop_stage_request;
drop_stage_request.set_cloud_unique_id(cloud_unique_id);
drop_stage_request.set_type(StagePB::INTERNAL);
drop_stage_request.set_mysql_user_id("root_id");
drop_stage_request.set_reason("Drop");
DropStageResponse drop_stage_response;
meta_service->drop_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&drop_stage_request, &drop_stage_response, nullptr);
ASSERT_EQ(drop_stage_response.status().code(), MetaServiceCode::OK);
// scan fdb has recycle_stage key
{
RecycleStageKeyInfo key_info0 {instance_id, ""};
RecycleStageKeyInfo key_info1 {instance_id, "{"};
std::string key0;
std::string key1;
recycle_stage_key(key_info0, &key0);
recycle_stage_key(key_info1, &key1);
std::unique_ptr<Transaction> txn;
std::string get_val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::unique_ptr<RangeGetIterator> it;
ASSERT_EQ(txn->get(key0, key1, &it), TxnErrorCode::TXN_OK);
int stage_cnt = 0;
do {
ASSERT_EQ(txn->get(key0, key1, &it), TxnErrorCode::TXN_OK);
while (it->has_next()) {
auto [k, v] = it->next();
++stage_cnt;
if (!it->has_next()) {
key0 = k;
}
}
key0.push_back('\x00');
} while (it->more());
ASSERT_EQ(stage_cnt, 1);
}
// get internal stage
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res2, nullptr);
ASSERT_EQ(res2.status().code(), MetaServiceCode::STAGE_NOT_FOUND);
// drop a non-exist internal stage
drop_stage_request.set_mysql_user_id("root_id2");
meta_service->drop_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&drop_stage_request, &drop_stage_response, nullptr);
ASSERT_EQ(drop_stage_response.status().code(), MetaServiceCode::STAGE_NOT_FOUND);
}
// test create and get external stage
{
// get an external stage with name
GetStageRequest get_stage_req;
get_stage_req.set_cloud_unique_id(cloud_unique_id);
get_stage_req.set_type(StagePB::EXTERNAL);
get_stage_req.set_stage_name("ex_name_1");
{
GetStageResponse res;
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::STAGE_NOT_FOUND);
}
// create 4 stages
for (auto i = 0; i < 4; ++i) {
StagePB stage;
stage.set_type(StagePB::EXTERNAL);
stage.set_stage_id("ex_id_" + std::to_string(i));
stage.set_name("ex_name_" + std::to_string(i));
if (i == 2) {
stage.set_access_type(StagePB::BUCKET_ACL);
} else if (i == 3) {
stage.set_access_type(StagePB::IAM);
}
stage.mutable_obj_info()->CopyFrom(obj);
CreateStageRequest create_stage_req;
create_stage_req.set_cloud_unique_id(cloud_unique_id);
create_stage_req.mutable_stage()->CopyFrom(stage);
CreateStageResponse create_stage_res;
meta_service->create_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&create_stage_req, &create_stage_res, nullptr);
ASSERT_EQ(create_stage_res.status().code(), MetaServiceCode::OK);
}
// stages number bigger than config
{
config::max_num_stages = 4;
StagePB stage;
stage.set_type(StagePB::INTERNAL);
stage.add_mysql_user_name("root1");
stage.add_mysql_user_id("root_id1");
stage.set_stage_id("internal_stage_id1");
CreateStageRequest create_stage_req;
create_stage_req.set_cloud_unique_id(cloud_unique_id);
create_stage_req.mutable_stage()->CopyFrom(stage);
CreateStageResponse create_stage_res;
meta_service->create_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&create_stage_req, &create_stage_res, nullptr);
ASSERT_EQ(create_stage_res.status().code(), MetaServiceCode::UNDEFINED_ERR);
}
// get an external stage with name
{
GetStageResponse res;
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(1, res.stage().size());
ASSERT_EQ("ex_id_1", res.stage().at(0).stage_id());
}
// get an external stage with name, type StagePB::BUCKET_ACL
{
get_stage_req.set_stage_name("ex_name_2");
GetStageResponse res;
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(1, res.stage().size());
ASSERT_EQ("ex_id_2", res.stage().at(0).stage_id());
}
// get an external stage with name, type StagePB::IAM
{
GetStageResponse res;
get_stage_req.set_stage_name("ex_name_3");
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(1, res.stage().size());
ASSERT_EQ("ex_id_3", res.stage().at(0).stage_id());
GetStageResponse res1;
std::unique_ptr<Transaction> txn;
TxnErrorCode err = meta_service->txn_kv()->create_txn(&txn);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
RamUserPB iam_user;
txn->put(system_meta_service_arn_info_key(), iam_user.SerializeAsString());
err = txn->commit();
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
LOG_INFO("err=", err);
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&get_stage_req, &res1, nullptr);
ASSERT_EQ(res1.status().code(), MetaServiceCode::OK);
ASSERT_EQ(1, res1.stage().size());
ASSERT_EQ("ex_id_3", res1.stage().at(0).stage_id());
}
GetStageRequest req;
req.set_cloud_unique_id(cloud_unique_id);
req.set_type(StagePB::EXTERNAL);
// get all stages
{
GetStageResponse res;
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(4, res.stage().size());
ASSERT_EQ("ex_id_0", res.stage().at(0).stage_id());
ASSERT_EQ("ex_id_1", res.stage().at(1).stage_id());
}
// drop one stage
{
DropStageRequest drop_stage_req;
drop_stage_req.set_cloud_unique_id(cloud_unique_id);
drop_stage_req.set_type(StagePB::EXTERNAL);
drop_stage_req.set_stage_name("tmp");
DropStageResponse res;
meta_service->drop_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&drop_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::STAGE_NOT_FOUND);
drop_stage_req.set_stage_name("ex_name_1");
meta_service->drop_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&drop_stage_req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
// get all stage
GetStageResponse get_stage_res;
meta_service->get_stage(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &get_stage_res, nullptr);
ASSERT_EQ(get_stage_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(3, get_stage_res.stage().size());
ASSERT_EQ("ex_name_0", get_stage_res.stage().at(0).name());
}
}
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
TEST(MetaServiceTest, GetIamTest) {
auto meta_service = get_meta_service();
brpc::Controller cntl;
auto cloud_unique_id = "test_cloud_unique_id";
std::string instance_id = "get_iam_test_instance_id";
[[maybe_unused]] auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->set_call_back("decrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* key = try_any_cast<std::string*>(args[0]);
*key = "test";
auto* ret = try_any_cast<int*>(args[1]);
*ret = 0;
});
sp->enable_processing();
config::arn_id = "iam_arn";
config::arn_ak = "iam_ak";
config::arn_sk = "iam_sk";
// create instance
{
ObjectStoreInfoPB obj;
obj.set_ak("123");
obj.set_sk("321");
obj.set_bucket("456");
obj.set_prefix("654");
obj.set_endpoint("789");
obj.set_region("987");
obj.set_external_endpoint("888");
obj.set_provider(ObjectStoreInfoPB::BOS);
RamUserPB ram_user;
ram_user.set_user_id("test_user_id");
ram_user.set_ak("test_ak");
ram_user.set_sk("test_sk");
CreateInstanceRequest req;
req.set_instance_id(instance_id);
req.set_user_id("test_user");
req.set_name("test_name");
req.mutable_ram_user()->CopyFrom(ram_user);
req.mutable_obj_info()->CopyFrom(obj);
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
GetIamRequest request;
request.set_cloud_unique_id(cloud_unique_id);
GetIamResponse response;
meta_service->get_iam(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &request,
&response, nullptr);
ASSERT_EQ(response.status().code(), MetaServiceCode::OK);
ASSERT_EQ(response.ram_user().user_id(), "test_user_id");
ASSERT_EQ(response.ram_user().ak(), "test_ak");
ASSERT_EQ(response.ram_user().sk(), "test_sk");
ASSERT_TRUE(response.ram_user().external_id().empty());
ASSERT_EQ(response.iam_user().user_id(), "iam_arn");
ASSERT_EQ(response.iam_user().external_id(), instance_id);
ASSERT_EQ(response.iam_user().ak(), "iam_ak");
ASSERT_EQ(response.iam_user().sk(), "iam_sk");
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
TEST(MetaServiceTest, AlterRamTest) {
auto meta_service = get_meta_service();
brpc::Controller cntl;
auto cloud_unique_id = "test_cloud_unique_id";
std::string instance_id = "alter_iam_test_instance_id";
[[maybe_unused]] auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->set_call_back("decrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* key = try_any_cast<std::string*>(args[0]);
*key = "test";
auto* ret = try_any_cast<int*>(args[1]);
*ret = 0;
});
sp->enable_processing();
config::arn_id = "iam_arn";
config::arn_ak = "iam_ak";
config::arn_sk = "iam_sk";
ObjectStoreInfoPB obj;
obj.set_ak("123");
obj.set_sk("321");
obj.set_bucket("456");
obj.set_prefix("654");
obj.set_endpoint("789");
obj.set_region("987");
obj.set_external_endpoint("888");
obj.set_provider(ObjectStoreInfoPB::BOS);
// create instance without ram user
CreateInstanceRequest create_instance_req;
create_instance_req.set_instance_id(instance_id);
create_instance_req.set_user_id("test_user");
create_instance_req.set_name("test_name");
create_instance_req.mutable_obj_info()->CopyFrom(obj);
CreateInstanceResponse create_instance_res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&create_instance_req, &create_instance_res, nullptr);
ASSERT_EQ(create_instance_res.status().code(), MetaServiceCode::OK);
// get iam and ram user
GetIamRequest request;
request.set_cloud_unique_id(cloud_unique_id);
GetIamResponse response;
meta_service->get_iam(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &request,
&response, nullptr);
ASSERT_EQ(response.status().code(), MetaServiceCode::OK);
ASSERT_EQ(response.has_ram_user(), false);
ASSERT_EQ(response.iam_user().user_id(), "iam_arn");
ASSERT_EQ(response.iam_user().ak(), "iam_ak");
ASSERT_EQ(response.iam_user().sk(), "iam_sk");
// alter ram user
RamUserPB ram_user;
ram_user.set_user_id("test_user_id");
ram_user.set_ak("test_ak");
ram_user.set_sk("test_sk");
AlterRamUserRequest alter_ram_user_request;
alter_ram_user_request.set_instance_id(instance_id);
alter_ram_user_request.mutable_ram_user()->CopyFrom(ram_user);
AlterRamUserResponse alter_ram_user_response;
meta_service->alter_ram_user(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&alter_ram_user_request, &alter_ram_user_response, nullptr);
// get iam and ram user
meta_service->get_iam(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &request,
&response, nullptr);
ASSERT_EQ(response.status().code(), MetaServiceCode::OK);
ASSERT_EQ(response.has_ram_user(), true);
ASSERT_EQ(response.ram_user().user_id(), "test_user_id");
ASSERT_EQ(response.ram_user().ak(), "test_ak");
ASSERT_EQ(response.ram_user().sk(), "test_sk");
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
std::string to_raw_string(std::string_view v) {
std::string ret;
ret.reserve(v.size() / 1.5);
while (!v.empty()) {
if (v[0] == '\\') {
if (v[1] == 'x') {
ret.push_back(unhex(std::string_view {v.data() + 2, 2})[0]);
v.remove_prefix(4);
} else if (v[1] == '\\') {
ret.push_back('\\');
v.remove_prefix(2);
} else {
std::abort();
}
continue;
}
ret.push_back(v[0]);
v.remove_prefix(1);
}
return ret;
}
TEST(MetaServiceTest, DecodeTest) {
// 504
std::string v1 =
R"(\x08\x00\x10\xa0[\x18\xb3[ \xde\xc5\xa4\x8e\xbd\xf0\x97\xc62(\xf4\x96\xe6\xb0\x070\x018\x02@\x02H\x0bX\x05`\xa0\x07h\xa0\x07p\xa0\x01\x88\x01\x00\xa0\x01\x86\x8b\x9a\x9b\x06\xaa\x01\x16\x08\xe6\x9e\x91\xa3\xfb\xbe\xf5\xf0\xc4\x01\x10\xfe\x8b\x90\xa7\xb5\xec\xd5\xc8\xbf\x01\xb0\x01\x01\xba\x0100200000000000071fb4aabb58c570cbcadb10857d3131b97\xc2\x01\x011\xc8\x01\x84\x8b\x9a\x9b\x06\xd0\x01\x85\x8b\x9a\x9b\x06\xda\x01\x04\x0a\x00\x12\x00\xe2\x01\xcd\x02\x08\x02\x121\x08\x00\x12\x06datek1\x1a\x04DATE \x01*\x04NONE0\x01:\x0a2022-01-01@\x00H\x00P\x03X\x03\x80\x01\x01\x12>\x08\x01\x12\x06datek2\x1a\x08DATETIME \x01*\x04NONE0\x01:\x132022-01-01 11:11:11@\x00H\x00P\x08X\x08\x80\x01\x01\x123\x08\x04\x12\x06datev3\x1a\x06DATEV2 \x01*\x04NONE0\x01:\x0a2022-01-01@\x00H\x00P\x04X\x04\x80\x01\x01\x120\x08\x02\x12\x06datev1\x1a\x04DATE \x00*\x03MAX0\x01:\x0a2022-01-01@\x00H\x00P\x03X\x03\x80\x01\x01\x12=\x08\x03\x12\x06datev2\x1a\x08DATETIME \x00*\x03MAX0\x01:\x132022-01-01 11:11:11@\x00H\x00P\x08X\x08\x80\x01\x01\x18\x03 \x80\x08(\x021\x00\x00\x00\x00\x00\x00\x00\x008\x00@\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01H\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01P\x00X\x02`\x05h\x00p\x00\xe8\x01\x85\xae\x9f\x9b\x06\x98\x03\x02)";
std::string val1 = to_raw_string(v1);
std::cout << "val1 size " << val1.size() << std::endl;
// 525
std::string v2 =
R"(\x08\x00\x10\xa0[\x18\xb3[ \x80\xb0\x85\xe3\xda\xcc\x8c\x0f(\xf4\x96\xe6\xb0\x070\x018\x01@\x0cH\x0cX\x00`\x00h\x00p\x00\x82\x01\x1e\x08\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x11datev3=2022-01-01\x88\x01\x01\x92\x01\x04\x08\x00\x10\x00\xa0\x01\x87\x8b\x9a\x9b\x06\xaa\x01\x16\x08\xe6\x9e\x91\xa3\xfb\xbe\xf5\xf0\xc4\x01\x10\xfe\x8b\x90\xa7\xb5\xec\xd5\xc8\xbf\x01\xb0\x01\x00\xba\x0100200000000000072fb4aabb58c570cbcadb10857d3131b97\xc8\x01\x87\x8b\x9a\x9b\x06\xd0\x01\x87\x8b\x9a\x9b\x06\xe2\x01\xcd\x02\x08\x02\x121\x08\x00\x12\x06datek1\x1a\x04DATE \x01*\x04NONE0\x01:\x0a2022-01-01@\x00H\x00P\x03X\x03\x80\x01\x01\x12>\x08\x01\x12\x06datek2\x1a\x08DATETIME \x01*\x04NONE0\x01:\x132022-01-01 11:11:11@\x00H\x00P\x08X\x08\x80\x01\x01\x123\x08\x04\x12\x06datev3\x1a\x06DATEV2 \x01*\x04NONE0\x01:\x0a2022-01-01@\x00H\x00P\x04X\x04\x80\x01\x01\x120\x08\x02\x12\x06datev1\x1a\x04DATE \x00*\x03MAX0\x01:\x0a2022-01-01@\x00H\x00P\x03X\x03\x80\x01\x01\x12=\x08\x03\x12\x06datev2\x1a\x08DATETIME \x00*\x03MAX0\x01:\x132022-01-01 11:11:11@\x00H\x00P\x08X\x08\x80\x01\x01\x18\x03 \x80\x08(\x021\x00\x00\x00\x00\x00\x00\x00\x008\x00@\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01H\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01P\x00X\x02`\x05h\x00p\x00\xe8\x01\x00\x98\x03\x02)";
std::string val2 = to_raw_string(v2);
std::cout << "val2 size " << val2.size() << std::endl;
[[maybe_unused]] std::string key1(
"\x01\x10meta\x00\x01\x10selectdb-cloud-"
"dev\x00\x01\x10rowset\x00\x01\x12\x00\x00\x00\x00\x00\x00-"
"\xb3\x12\x00\x00\x00\x00\x00\x00\x00\x0b",
56);
[[maybe_unused]] std::string key2(
"\x01\x10meta\x00\x01\x10selectdb-cloud-"
"dev\x00\x01\x10rowset\x00\x01\x12\x00\x00\x00\x00\x00\x00-"
"\xb3\x12\x00\x00\x00\x00\x00\x00\x00\x0c",
56);
std::cout << "key1 " << key1.size() << " " << hex(key1) << std::endl;
std::cout << "key2 " << key2.size() << " " << hex(key2) << std::endl;
doris::RowsetMetaCloudPB rowset1;
doris::RowsetMetaCloudPB rowset2;
rowset1.ParseFromString(val1);
rowset2.ParseFromString(val2);
std::cout << "rowset1=" << proto_to_json(rowset1) << std::endl;
std::cout << "rowset2=" << proto_to_json(rowset2) << std::endl;
}
void get_tablet_stats(MetaServiceProxy* meta_service, int64_t table_id, int64_t index_id,
int64_t partition_id, int64_t tablet_id, GetTabletStatsResponse& res) {
brpc::Controller cntl;
GetTabletStatsRequest req;
auto idx = req.add_tablet_idx();
idx->set_table_id(table_id);
idx->set_index_id(index_id);
idx->set_partition_id(partition_id);
idx->set_tablet_id(tablet_id);
meta_service->get_tablet_stats(&cntl, &req, &res, nullptr);
}
TEST(MetaServiceTest, UpdateTablet) {
auto meta_service = get_meta_service();
std::string cloud_unique_id = "test_cloud_unique_id";
constexpr auto table_id = 11231, index_id = 11232, partition_id = 11233, tablet_id1 = 11234,
tablet_id2 = 21234;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id1));
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id2));
auto get_and_check_tablet_meta = [&](int tablet_id, int64_t ttl_seconds, bool in_memory,
bool is_persistent) {
brpc::Controller cntl;
GetTabletRequest req;
req.set_cloud_unique_id(cloud_unique_id);
req.set_tablet_id(tablet_id);
GetTabletResponse resp;
meta_service->get_tablet(&cntl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::OK) << tablet_id;
EXPECT_EQ(resp.tablet_meta().ttl_seconds(), ttl_seconds);
EXPECT_EQ(resp.tablet_meta().is_in_memory(), in_memory);
EXPECT_EQ(resp.tablet_meta().is_persistent(), is_persistent);
};
get_and_check_tablet_meta(tablet_id1, 0, false, false);
get_and_check_tablet_meta(tablet_id2, 0, false, false);
{
brpc::Controller cntl;
UpdateTabletRequest req;
UpdateTabletResponse resp;
req.set_cloud_unique_id(cloud_unique_id);
TabletMetaInfoPB* tablet_meta_info = req.add_tablet_meta_infos();
tablet_meta_info->set_tablet_id(tablet_id1);
tablet_meta_info->set_ttl_seconds(300);
tablet_meta_info = req.add_tablet_meta_infos();
tablet_meta_info->set_tablet_id(tablet_id2);
tablet_meta_info->set_ttl_seconds(3000);
meta_service->update_tablet(&cntl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::OK);
}
get_and_check_tablet_meta(tablet_id1, 300, false, false);
get_and_check_tablet_meta(tablet_id2, 3000, false, false);
{
brpc::Controller cntl;
UpdateTabletRequest req;
UpdateTabletResponse resp;
req.set_cloud_unique_id(cloud_unique_id);
TabletMetaInfoPB* tablet_meta_info = req.add_tablet_meta_infos();
tablet_meta_info->set_tablet_id(tablet_id1);
tablet_meta_info->set_is_in_memory(true);
meta_service->update_tablet(&cntl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::OK);
}
{
brpc::Controller cntl;
UpdateTabletRequest req;
UpdateTabletResponse resp;
req.set_cloud_unique_id(cloud_unique_id);
TabletMetaInfoPB* tablet_meta_info = req.add_tablet_meta_infos();
tablet_meta_info->set_tablet_id(tablet_id1);
tablet_meta_info->set_is_persistent(true);
meta_service->update_tablet(&cntl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::OK);
}
get_and_check_tablet_meta(tablet_id1, 300, true, true);
}
TEST(MetaServiceTest, GetTabletStatsTest) {
auto meta_service = get_meta_service();
constexpr auto table_id = 10001, index_id = 10002, partition_id = 10003, tablet_id = 10004;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
GetTabletStatsResponse res;
get_tablet_stats(meta_service.get(), table_id, index_id, partition_id, tablet_id, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.tablet_stats_size(), 1);
EXPECT_EQ(res.tablet_stats(0).data_size(), 0);
EXPECT_EQ(res.tablet_stats(0).num_rows(), 0);
EXPECT_EQ(res.tablet_stats(0).num_rowsets(), 1);
EXPECT_EQ(res.tablet_stats(0).num_segments(), 0);
EXPECT_EQ(res.tablet_stats(0).index_size(), 0);
EXPECT_EQ(res.tablet_stats(0).segment_size(), 0);
// Insert rowset
config::split_tablet_stats = false;
ASSERT_NO_FATAL_FAILURE(
insert_rowset(meta_service.get(), 10000, "label1", table_id, partition_id, tablet_id));
ASSERT_NO_FATAL_FAILURE(
insert_rowset(meta_service.get(), 10000, "label2", table_id, partition_id, tablet_id));
config::split_tablet_stats = true;
ASSERT_NO_FATAL_FAILURE(
insert_rowset(meta_service.get(), 10000, "label3", table_id, partition_id, tablet_id));
ASSERT_NO_FATAL_FAILURE(
insert_rowset(meta_service.get(), 10000, "label4", table_id, partition_id, tablet_id));
// Check tablet stats kv
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string data_size_key, data_size_val;
stats_tablet_data_size_key({mock_instance, table_id, index_id, partition_id, tablet_id},
&data_size_key);
ASSERT_EQ(txn->get(data_size_key, &data_size_val), TxnErrorCode::TXN_OK);
EXPECT_EQ(*(int64_t*)data_size_val.data(), 22000);
std::string index_size_key, index_size_val;
stats_tablet_index_size_key({mock_instance, table_id, index_id, partition_id, tablet_id},
&index_size_key);
ASSERT_EQ(txn->get(index_size_key, &index_size_val), TxnErrorCode::TXN_OK);
EXPECT_EQ(*(int64_t*)index_size_val.data(), 2000);
std::string segment_size_key, segment_size_val;
stats_tablet_segment_size_key({mock_instance, table_id, index_id, partition_id, tablet_id},
&segment_size_key);
ASSERT_EQ(txn->get(segment_size_key, &segment_size_val), TxnErrorCode::TXN_OK);
EXPECT_EQ(*(int64_t*)segment_size_val.data(), 20000);
std::string num_rows_key, num_rows_val;
stats_tablet_num_rows_key({mock_instance, table_id, index_id, partition_id, tablet_id},
&num_rows_key);
ASSERT_EQ(txn->get(num_rows_key, &num_rows_val), TxnErrorCode::TXN_OK);
EXPECT_EQ(*(int64_t*)num_rows_val.data(), 200);
std::string num_rowsets_key, num_rowsets_val;
stats_tablet_num_rowsets_key({mock_instance, table_id, index_id, partition_id, tablet_id},
&num_rowsets_key);
ASSERT_EQ(txn->get(num_rowsets_key, &num_rowsets_val), TxnErrorCode::TXN_OK);
EXPECT_EQ(*(int64_t*)num_rowsets_val.data(), 2);
std::string num_segs_key, num_segs_val;
stats_tablet_num_segs_key({mock_instance, table_id, index_id, partition_id, tablet_id},
&num_segs_key);
ASSERT_EQ(txn->get(num_segs_key, &num_segs_val), TxnErrorCode::TXN_OK);
EXPECT_EQ(*(int64_t*)num_segs_val.data(), 2);
// Get tablet stats
res.Clear();
get_tablet_stats(meta_service.get(), table_id, index_id, partition_id, tablet_id, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.tablet_stats_size(), 1);
EXPECT_EQ(res.tablet_stats(0).data_size(), 44000);
EXPECT_EQ(res.tablet_stats(0).num_rows(), 400);
EXPECT_EQ(res.tablet_stats(0).num_rowsets(), 5);
EXPECT_EQ(res.tablet_stats(0).num_segments(), 4);
EXPECT_EQ(res.tablet_stats(0).index_size(), 4000);
EXPECT_EQ(res.tablet_stats(0).segment_size(), 40000);
}
void remove_delete_bitmap_lock(MetaServiceProxy* meta_service, int64_t table_id) {
std::string lock_key = meta_delete_bitmap_update_lock_key({"test_instance", table_id, -1});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->remove(lock_key);
std::string tablet_job_key_begin = mow_tablet_job_key({"test_instance", table_id, 0});
std::string tablet_job_key_end = mow_tablet_job_key({"test_instance", table_id, INT64_MAX});
txn->remove(tablet_job_key_begin, tablet_job_key_end);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
}
void testGetDeleteBitmapUpdateLock(int lock_version, int job_lock_id) {
config::delete_bitmap_lock_v2_white_list = lock_version == 1 ? "" : "*";
auto meta_service = get_meta_service();
[[maybe_unused]] auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
remove_delete_bitmap_lock(meta_service.get(), 1);
remove_delete_bitmap_lock(meta_service.get(), 2);
int64_t table_id = 9;
// case 1: lock key does not exist, get and remove load lock
brpc::Controller cntl;
GetDeleteBitmapUpdateLockRequest req;
GetDeleteBitmapUpdateLockResponse res;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_table_id(table_id);
req.add_partition_ids(123);
req.set_expiration(5);
req.set_lock_id(888);
req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
RemoveDeleteBitmapUpdateLockRequest remove_req;
RemoveDeleteBitmapUpdateLockResponse remove_res;
remove_req.set_cloud_unique_id("test_cloud_unique_id");
remove_req.set_table_id(table_id);
remove_req.set_lock_id(888);
remove_req.set_initiator(-1);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_req, &remove_res,
nullptr);
ASSERT_EQ(remove_res.status().code(), MetaServiceCode::OK);
// case 2: lock key does not exist, get and remove compaction lock
req.add_partition_ids(123);
req.set_expiration(600);
req.set_lock_id(job_lock_id);
req.set_initiator(100);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
remove_req.set_tablet_id(2);
remove_req.set_lock_id(job_lock_id);
remove_req.set_initiator(100);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_req, &remove_res,
nullptr);
ASSERT_EQ(remove_res.status().code(), MetaServiceCode::OK);
// case 3: lock key owned by load1, load2 get lock
req.add_partition_ids(123);
req.set_expiration(600);
req.set_lock_id(888);
req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
req.set_lock_id(889);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::LOCK_CONFLICT);
// case 4: lock key owned by load1, compaction1 get lock
req.set_lock_id(job_lock_id);
req.set_initiator(100);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::LOCK_CONFLICT);
remove_req.set_tablet_id(2);
remove_req.set_lock_id(888);
remove_req.set_initiator(-1);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_req, &remove_res,
nullptr);
ASSERT_EQ(remove_res.status().code(), MetaServiceCode::OK);
// case 5: lock key owned by load1 but expired, load2 get lock
req.add_partition_ids(123);
req.set_expiration(1);
req.set_lock_id(888);
req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sleep(2);
req.set_lock_id(889);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
remove_req.set_lock_id(889);
remove_req.set_initiator(-1);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_req, &remove_res,
nullptr);
ASSERT_EQ(remove_res.status().code(), MetaServiceCode::OK);
// case 6: lock key owned by load1 but expired, compaction1 get lock
req.add_partition_ids(123);
req.set_expiration(1);
req.set_lock_id(888);
req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sleep(2);
req.set_lock_id(job_lock_id);
req.set_initiator(888);
req.set_expiration(1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
remove_req.set_lock_id(job_lock_id);
remove_req.set_initiator(888);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_req, &remove_res,
nullptr);
ASSERT_EQ(remove_res.status().code(), MetaServiceCode::OK);
// case 7: lock key owned by compaction, new compaction get lock
req.set_lock_id(job_lock_id);
req.set_initiator(100);
req.set_expiration(100);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
req.set_lock_id(job_lock_id);
req.set_initiator(101);
req.set_expiration(1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
// new compaction get lock again
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
// case 8: lock key owned by compaction, load1 get lock
req.set_lock_id(888);
req.set_initiator(-1);
req.set_expiration(60);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::LOCK_CONFLICT);
remove_req.set_lock_id(job_lock_id);
remove_req.set_initiator(100);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_req, &remove_res,
nullptr);
ASSERT_EQ(remove_res.status().code(), MetaServiceCode::OK);
// case 9: lock key owned by compaction but all expired (101 900), load1 get lock
req.set_table_id(table_id);
req.set_lock_id(job_lock_id);
req.set_initiator(900);
req.set_expiration(1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sleep(2);
req.set_table_id(table_id);
req.set_lock_id(888);
req.set_initiator(-1);
req.set_expiration(60);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
remove_req.set_lock_id(888);
remove_req.set_initiator(-1);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_req, &remove_res,
nullptr);
ASSERT_EQ(remove_res.status().code(), MetaServiceCode::OK);
// case 10: lock key owned by compaction but all expired (101 900), schema change get lock
req.set_table_id(table_id);
req.set_lock_id(job_lock_id);
req.set_initiator(900);
req.set_expiration(1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sleep(2);
req.set_table_id(table_id);
int other_job_lock_id = job_lock_id == COMPACTION_DELETE_BITMAP_LOCK_ID
? SCHEMA_CHANGE_DELETE_BITMAP_LOCK_ID
: COMPACTION_DELETE_BITMAP_LOCK_ID;
req.set_lock_id(other_job_lock_id);
req.set_initiator(100);
req.set_expiration(1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
remove_req.set_lock_id(other_job_lock_id);
remove_req.set_initiator(100);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_req, &remove_res,
nullptr);
ASSERT_EQ(remove_res.status().code(), MetaServiceCode::OK);
// case 11: lock by schema change but expired, compaction get lock but txn commit conflict, do fast retry
sp->set_call_back("get_delete_bitmap_update_lock:commit:conflict", [&](auto&& args) {
auto* first_retry = try_any_cast<bool*>(args[0]);
auto lock_id = (try_any_cast<const GetDeleteBitmapUpdateLockRequest*>(args[1]))->lock_id();
if (*first_retry && is_job_delete_bitmap_lock_id(lock_id)) {
*try_any_cast<TxnErrorCode*>(args[2]) = TxnErrorCode::TXN_CONFLICT;
} else {
*try_any_cast<TxnErrorCode*>(args[2]) = TxnErrorCode::TXN_OK;
}
});
sp->enable_processing();
req.set_lock_id(job_lock_id);
req.set_initiator(100);
req.set_expiration(10);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
remove_req.set_lock_id(job_lock_id);
remove_req.set_initiator(100);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_req, &remove_res,
nullptr);
ASSERT_EQ(remove_res.status().code(), MetaServiceCode::OK);
// case 12: lock by load but expired, compaction get lock but txn commit conflict, do fast retry
req.set_lock_id(300);
req.set_initiator(-1);
req.set_expiration(1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sleep(2);
req.set_lock_id(job_lock_id);
req.set_initiator(100);
req.set_expiration(10);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
remove_delete_bitmap_lock(meta_service.get(), table_id);
// case 13: lock key does not exist, compaction get lock but txn commit conflict, do fast retry
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
remove_delete_bitmap_lock(meta_service.get(), table_id);
}
TEST(MetaServiceTest, GetDeleteBitmapUpdateLock) {
testGetDeleteBitmapUpdateLock(2, COMPACTION_DELETE_BITMAP_LOCK_ID);
testGetDeleteBitmapUpdateLock(2, SCHEMA_CHANGE_DELETE_BITMAP_LOCK_ID);
testGetDeleteBitmapUpdateLock(1, COMPACTION_DELETE_BITMAP_LOCK_ID);
testGetDeleteBitmapUpdateLock(1, SCHEMA_CHANGE_DELETE_BITMAP_LOCK_ID);
}
TEST(MetaServiceTest, GetDeleteBitmapUpdateLockNoReadStats) {
auto meta_service = get_meta_service();
brpc::Controller cntl;
GetDeleteBitmapUpdateLockRequest req;
GetDeleteBitmapUpdateLockResponse res;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_table_id(111);
req.add_partition_ids(123);
req.set_expiration(5);
req.set_lock_id(888);
req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
// same lock_id
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
// different lock_id
req.set_lock_id(999);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::LOCK_CONFLICT);
// lock expired
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_table_id(222);
req.set_expiration(0);
req.set_lock_id(666);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sleep(1);
req.set_lock_id(667);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
TEST(MetaServiceTest, GetDeleteBitmapUpdateLockTabletStatsNormal) {
auto meta_service = get_meta_service();
bool enable_batch_get_mow_tablet_stats_and_meta_vals[] = {false, true};
for (bool val : enable_batch_get_mow_tablet_stats_and_meta_vals) {
config::enable_batch_get_mow_tablet_stats_and_meta = val;
std::string instance_id = "test_get_delete_bitmap_update_lock_normal";
[[maybe_unused]] auto* sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->enable_processing();
int64_t db_id = 1000;
int64_t table_id = 2001;
int64_t index_id = 3001;
// [(partition_id, tablet_id)]
std::vector<std::array<int64_t, 2>> tablet_idxes {
{70001, 12345}, {80001, 3456}, {90001, 6789}};
add_tablet_metas(meta_service.get(), instance_id, table_id, index_id, tablet_idxes);
GetDeleteBitmapUpdateLockResponse res;
get_delete_bitmap_update_lock(meta_service.get(), res, db_id, table_id, index_id,
tablet_idxes, 5, 999999, -1, true);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.base_compaction_cnts().size(), tablet_idxes.size());
for (const auto& base_compaction_cnt : res.base_compaction_cnts()) {
ASSERT_EQ(base_compaction_cnt, 10);
}
ASSERT_EQ(res.cumulative_compaction_cnts().size(), tablet_idxes.size());
for (const auto& cumu_compaction_cnt : res.cumulative_compaction_cnts()) {
ASSERT_EQ(cumu_compaction_cnt, 20);
}
ASSERT_EQ(res.cumulative_points().size(), tablet_idxes.size());
for (const auto& cumulative_point : res.cumulative_points()) {
ASSERT_EQ(cumulative_point, 30);
}
}
}
TEST(MetaServiceTest, GetDeleteBitmapUpdateLockTabletStatsLockExpired) {
auto meta_service = get_meta_service();
bool enable_batch_get_mow_tablet_stats_and_meta_vals[] = {false, true};
for (bool val : enable_batch_get_mow_tablet_stats_and_meta_vals) {
config::enable_batch_get_mow_tablet_stats_and_meta = val;
// 2.1 abnormal path, lock has been expired and taken by another load/compaction during
// the reading of tablet stats
std::string instance_id = "test_get_delete_bitmap_update_lock_abnormal1";
[[maybe_unused]] auto* sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->set_call_back("check_delete_bitmap_lock.set_lock_info", [&](auto&& args) {
auto* lock_info = try_any_cast<DeleteBitmapUpdateLockPB*>(args[0]);
// simulate that lock_id has been modified by another load
lock_info->set_lock_id(345);
LOG(INFO) << "change lock_info.lock_id to 345, lock_info=" << lock_info->DebugString();
});
sp->enable_processing();
int64_t db_id = 1000;
int64_t table_id = 2001;
int64_t index_id = 3001;
// [(partition_id, tablet_id)]
std::vector<std::array<int64_t, 2>> tablet_idxes {
{70001, 12345}, {80001, 3456}, {90001, 6789}};
add_tablet_metas(meta_service.get(), instance_id, table_id, index_id, tablet_idxes);
GetDeleteBitmapUpdateLockResponse res;
get_delete_bitmap_update_lock(meta_service.get(), res, db_id, table_id, index_id,
tablet_idxes, 5, 999999, -1, true);
EXPECT_EQ(res.status().code(), MetaServiceCode::LOCK_EXPIRED);
EXPECT_EQ(res.base_compaction_cnts().size(), 3);
EXPECT_EQ(res.cumulative_compaction_cnts().size(), 3);
EXPECT_EQ(res.cumulative_points().size(), 3);
}
for (bool val : enable_batch_get_mow_tablet_stats_and_meta_vals) {
config::enable_batch_get_mow_tablet_stats_and_meta = val;
// 2.2 abnormal path, lock has been taken by another load/compaction and been released during
// the reading of tablet stats
std::string instance_id = "test_get_delete_bitmap_update_lock_abnormal2";
[[maybe_unused]] auto* sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->set_call_back("check_delete_bitmap_lock.inject_get_lock_key_err", [&](auto&& args) {
auto* err = try_any_cast<TxnErrorCode*>(args[0]);
// the lock has been taken by another load and been released,
// so the delete bitmap update lock KV will be removed
*err = TxnErrorCode::TXN_KEY_NOT_FOUND;
});
sp->enable_processing();
int64_t db_id = 1000;
int64_t table_id = 2001;
int64_t index_id = 3001;
// [(partition_id, tablet_id)]
std::vector<std::array<int64_t, 2>> tablet_idxes {
{70001, 12345}, {80001, 3456}, {90001, 6789}};
add_tablet_metas(meta_service.get(), instance_id, table_id, index_id, tablet_idxes);
GetDeleteBitmapUpdateLockResponse res;
get_delete_bitmap_update_lock(meta_service.get(), res, db_id, table_id, index_id,
tablet_idxes, 5, 999999, -1, true);
ASSERT_EQ(res.status().code(), MetaServiceCode::LOCK_EXPIRED);
}
}
TEST(MetaServiceTest, GetDeleteBitmapUpdateLockTabletStatsError) {
auto meta_service = get_meta_service();
bool enable_batch_get_mow_tablet_stats_and_meta_vals[] = {false, true};
for (bool val : enable_batch_get_mow_tablet_stats_and_meta_vals) {
config::enable_batch_get_mow_tablet_stats_and_meta = val;
// 2.3 abnormal path, meeting error when reading tablets' stats
std::string instance_id = "test_get_delete_bitmap_update_lock_abnormal3";
[[maybe_unused]] auto* sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
TxnErrorCode injected_error_code {TxnErrorCode::TXN_KEY_NOT_FOUND};
sp->set_call_back("get_delete_bitmap_update_lock.get_compaction_cnts_inject_error",
[&](auto&& args) {
auto* err = try_any_cast<TxnErrorCode*>(args[0]);
*err = injected_error_code;
});
sp->enable_processing();
int64_t db_id = 1000;
int64_t table_id = 2001;
int64_t index_id = 3001;
// [(partition_id, tablet_id)]
std::vector<std::array<int64_t, 2>> tablet_idxes {
{70001, 12345}, {80001, 3456}, {90001, 6789}};
add_tablet_metas(meta_service.get(), instance_id, table_id, index_id, tablet_idxes);
GetDeleteBitmapUpdateLockResponse res;
get_delete_bitmap_update_lock(meta_service.get(), res, db_id, table_id, index_id,
tablet_idxes, 5, 999999, -1, true);
ASSERT_EQ(res.status().code(), MetaServiceCode::KV_TXN_GET_ERR);
}
for (bool val : enable_batch_get_mow_tablet_stats_and_meta_vals) {
config::enable_batch_get_mow_tablet_stats_and_meta = val;
// 2.4 abnormal path, meeting TXN_TOO_OLD error when reading tablets' stats,
// this should not fail if lock is not expired
std::string instance_id = "test_get_delete_bitmap_update_lock_abnormal4";
[[maybe_unused]] auto* sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
int counter = 0;
sp->set_call_back("get_delete_bitmap_update_lock.get_compaction_cnts_inject_error",
[&](auto&& args) {
if (counter++ % 2 == 0) {
auto* err = try_any_cast<TxnErrorCode*>(args[0]);
*err = TxnErrorCode::TXN_TOO_OLD;
}
});
sp->enable_processing();
int64_t db_id = 1000;
int64_t table_id = 2001;
int64_t index_id = 3001;
// [(partition_id, tablet_id)]
std::vector<std::array<int64_t, 2>> tablet_idxes;
for (int i = 0; i < 20; i++) {
int64_t partition_id = 70000 + i;
int64_t tablet_id = 80000 + i;
tablet_idxes.push_back({partition_id, tablet_id});
}
add_tablet_metas(meta_service.get(), instance_id, table_id, index_id, tablet_idxes);
GetDeleteBitmapUpdateLockResponse res;
get_delete_bitmap_update_lock(meta_service.get(), res, db_id, table_id, index_id,
tablet_idxes, 5, 999999, -1, true);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.base_compaction_cnts().size(), tablet_idxes.size());
for (const auto& base_compaction_cnt : res.base_compaction_cnts()) {
ASSERT_EQ(base_compaction_cnt, 10);
}
ASSERT_EQ(res.cumulative_compaction_cnts().size(), tablet_idxes.size());
for (const auto& cumu_compaction_cnt : res.cumulative_compaction_cnts()) {
ASSERT_EQ(cumu_compaction_cnt, 20);
}
ASSERT_EQ(res.cumulative_points().size(), tablet_idxes.size());
for (const auto& cumulative_point : res.cumulative_points()) {
ASSERT_EQ(cumulative_point, 30);
}
}
for (bool val : enable_batch_get_mow_tablet_stats_and_meta_vals) {
config::enable_batch_get_mow_tablet_stats_and_meta = val;
// 2.5 abnormal path, meeting error when reading tablets' meta
std::string instance_id = "test_get_delete_bitmap_update_lock_abnormal5";
[[maybe_unused]] auto* sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->enable_processing();
int64_t db_id = 1000;
int64_t table_id = 2001;
int64_t index_id = 3001;
// [(partition_id, tablet_id)]
std::vector<std::array<int64_t, 2>> tablet_idxes {
{70001, 12345}, {80001, 3456}, {90001, 6789}};
add_tablet_metas(meta_service.get(), instance_id, table_id, index_id, tablet_idxes, true);
GetDeleteBitmapUpdateLockResponse res;
get_delete_bitmap_update_lock(meta_service.get(), res, db_id, table_id, index_id,
tablet_idxes, 5, 999999, -1, true);
ASSERT_EQ(res.status().code(), MetaServiceCode::TABLET_NOT_FOUND);
}
}
static std::string generate_random_string(int length) {
std::string char_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
std::random_device rd;
std::mt19937 generator(rd());
std::uniform_int_distribution<int> distribution(0, char_set.length() - 1);
std::string randomString;
for (int i = 0; i < length; ++i) {
randomString += char_set[distribution(generator)];
}
return randomString;
}
TEST(MetaServiceTest, UpdateDeleteBitmapWithBigKeys) {
auto meta_service = get_meta_service();
// get delete bitmap update lock
brpc::Controller cntl;
GetDeleteBitmapUpdateLockRequest get_lock_req;
GetDeleteBitmapUpdateLockResponse get_lock_res;
get_lock_req.set_cloud_unique_id("test_cloud_unique_id");
get_lock_req.set_table_id(1999);
get_lock_req.add_partition_ids(123);
get_lock_req.set_expiration(5);
get_lock_req.set_lock_id(-1);
get_lock_req.set_initiator(100);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
// v1 update delete bitmap
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(1999);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(-1);
update_delete_bitmap_req.set_initiator(100);
update_delete_bitmap_req.set_tablet_id(333);
std::string large_value = generate_random_string(300 * 1000 * 3);
DeleteBitmapPB delete_bitmap_pb;
auto num = 100000;
for (int i = 0; i < num; i++) {
update_delete_bitmap_req.add_rowset_ids("0200000003ea308a3647dbea83220ed4b8897f2288244a91");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(i);
update_delete_bitmap_req.add_segment_delete_bitmaps(i % 500 == 0 ? large_value : "1");
delete_bitmap_pb.add_rowset_ids("0200000003ea308a3647dbea83220ed4b8897f2288244a91");
delete_bitmap_pb.add_segment_ids(0);
delete_bitmap_pb.add_versions(i);
delete_bitmap_pb.add_segment_delete_bitmaps(i % 500 == 0 ? large_value : "1");
}
// v2 update delete bitmap
DeleteBitmapStoragePB delete_bitmap_storage_pb;
delete_bitmap_storage_pb.set_store_in_fdb(true);
*(delete_bitmap_storage_pb.mutable_delete_bitmap()) = std::move(delete_bitmap_pb);
*(update_delete_bitmap_req.add_delete_bitmap_storages()) = std::move(delete_bitmap_storage_pb);
update_delete_bitmap_req.add_delta_rowset_ids(
"0200000003ea308a3647dbea83220ed4b8897f2288244a91");
meta_service->update_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res,
nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
// get delete bitmap v1
GetDeleteBitmapRequest get_delete_bitmap_req;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(333);
get_delete_bitmap_req.add_rowset_ids("0200000003ea308a3647dbea83220ed4b8897f2288244a91");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(num);
{
// get exceed max_get_delete_bitmap_byte limit
auto max_get_delete_bitmap_byte = config::max_get_delete_bitmap_byte;
config::max_get_delete_bitmap_byte = 1;
GetDeleteBitmapResponse get_delete_bitmap_res;
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::KV_TXN_GET_ERR);
config::max_get_delete_bitmap_byte = max_get_delete_bitmap_byte;
}
{
GetDeleteBitmapResponse get_delete_bitmap_res;
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), num);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), num);
ASSERT_EQ(get_delete_bitmap_res.segment_ids_size(), num);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), num);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(0), large_value);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(1), "1");
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(500), large_value);
}
// get delete bitmap v2
{
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_store_version(2);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.delete_bitmap_storages_size(), 1);
auto& delete_bitmap_storage = get_delete_bitmap_res.delete_bitmap_storages(0);
ASSERT_TRUE(delete_bitmap_storage.store_in_fdb());
auto& delete_bitmap_pb = delete_bitmap_storage.delete_bitmap();
ASSERT_EQ(delete_bitmap_pb.rowset_ids_size(), num);
ASSERT_EQ(delete_bitmap_pb.versions_size(), num);
ASSERT_EQ(delete_bitmap_pb.segment_ids_size(), num);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps_size(), num);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(0), large_value);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(1), "1");
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(500), large_value);
}
}
static void set_partition_version(MetaServiceProxy* meta_service, std::string_view instance_id,
int64_t db_id, int64_t table_id, int64_t partition_id,
int64_t version, std::vector<int64_t> pending_txn_ids = {}) {
std::string ver_key = partition_version_key({instance_id, db_id, table_id, partition_id});
std::string ver_val;
VersionPB version_pb;
version_pb.set_version(version);
if (!pending_txn_ids.empty()) {
for (auto txn_id : pending_txn_ids) {
version_pb.add_pending_txn_ids(txn_id);
}
}
ASSERT_TRUE(version_pb.SerializeToString(&ver_val));
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(ver_key, ver_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
}
static void begin_txn_and_commit_rowset(MetaServiceProxy* meta_service, const std::string& label,
int64_t db_id, int64_t table_id, int64_t partition_id,
int64_t tablet_id, int64_t* txn_id) {
begin_txn(meta_service, db_id, label, table_id, *txn_id);
CreateRowsetResponse res;
auto rowset = create_rowset(*txn_id, tablet_id, partition_id);
prepare_rowset(meta_service, rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
res.Clear();
commit_rowset(meta_service, rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
static void get_delete_bitmap_update_lock(MetaServiceProxy* meta_service, int64_t table_id,
int64_t partition_id, int64_t lock_id,
int64_t initiator) {
brpc::Controller cntl;
GetDeleteBitmapUpdateLockRequest get_lock_req;
GetDeleteBitmapUpdateLockResponse get_lock_res;
get_lock_req.set_cloud_unique_id("test_cloud_unique_id");
get_lock_req.set_table_id(table_id);
get_lock_req.add_partition_ids(partition_id);
get_lock_req.set_expiration(5);
get_lock_req.set_lock_id(lock_id);
get_lock_req.set_initiator(initiator);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
}
static void update_delete_bitmap(MetaServiceProxy* meta_service,
UpdateDeleteBitmapRequest& update_delete_bitmap_req,
UpdateDeleteBitmapResponse& update_delete_bitmap_res,
int64_t table_id, int64_t partition_id, int64_t lock_id,
int64_t initiator, int64_t tablet_id, int64_t txn_id,
int64_t next_visible_version, std::string data = "1111") {
brpc::Controller cntl;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(table_id);
update_delete_bitmap_req.set_partition_id(partition_id);
update_delete_bitmap_req.set_lock_id(lock_id);
update_delete_bitmap_req.set_initiator(initiator);
update_delete_bitmap_req.set_tablet_id(tablet_id);
update_delete_bitmap_req.set_txn_id(txn_id);
update_delete_bitmap_req.set_next_visible_version(next_visible_version);
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(next_visible_version);
update_delete_bitmap_req.add_segment_delete_bitmaps(data);
meta_service->update_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res,
nullptr);
}
TEST(MetaServiceTest, UpdateDeleteBitmapCheckPartitionVersion) {
auto meta_service = get_meta_service();
brpc::Controller cntl;
extern std::string get_instance_id(const std::shared_ptr<ResourceManager>& rc_mgr,
const std::string& cloud_unique_id);
auto instance_id = get_instance_id(meta_service->resource_mgr(), "test_cloud_unique_id");
{
// 1. normal path
// 1.1 has partition version and request version matches
int64_t db_id = 999;
int64_t table_id = 1001;
int64_t index_id = 4001;
int64_t t1p1 = 2001;
int64_t tablet_id = 3001;
int64_t initiator = -1;
int64_t cur_max_version = 100;
int64_t txn_id;
ASSERT_NO_FATAL_FAILURE(create_tablet_with_db_id(meta_service.get(), db_id, table_id,
index_id, t1p1, tablet_id));
begin_txn_and_commit_rowset(meta_service.get(), "label11", db_id, table_id, t1p1, tablet_id,
&txn_id);
int64_t lock_id = txn_id;
get_delete_bitmap_update_lock(meta_service.get(), table_id, t1p1, lock_id, initiator);
set_partition_version(meta_service.get(), instance_id, db_id, table_id, t1p1,
cur_max_version);
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap(meta_service.get(), update_delete_bitmap_req, update_delete_bitmap_res,
table_id, t1p1, lock_id, initiator, tablet_id, txn_id,
cur_max_version + 1);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
}
{
// 1. normal path
// 1.2 does not have partition version KV and request version matches
int64_t db_id = 999;
int64_t table_id = 1002;
int64_t index_id = 4001;
int64_t t1p1 = 2001;
int64_t tablet_id = 3001;
int64_t initiator = -1;
int64_t txn_id;
ASSERT_NO_FATAL_FAILURE(create_tablet_with_db_id(meta_service.get(), db_id, table_id,
index_id, t1p1, tablet_id));
begin_txn_and_commit_rowset(meta_service.get(), "label12", db_id, table_id, t1p1, tablet_id,
&txn_id);
int64_t lock_id = txn_id;
get_delete_bitmap_update_lock(meta_service.get(), table_id, t1p1, lock_id, initiator);
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap(meta_service.get(), update_delete_bitmap_req, update_delete_bitmap_res,
table_id, t1p1, lock_id, initiator, tablet_id, txn_id, 2);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
}
{
// 1. normal path
// 1.3 has partition version and pending txn, and request version matches
int64_t db_id = 999;
int64_t table_id = 1003;
int64_t index_id = 4001;
int64_t t1p1 = 2001;
int64_t tablet_id = 3001;
int64_t initiator = -1;
int64_t cur_max_version = 120;
int64_t txn_id;
ASSERT_NO_FATAL_FAILURE(create_tablet_with_db_id(meta_service.get(), db_id, table_id,
index_id, t1p1, tablet_id));
begin_txn_and_commit_rowset(meta_service.get(), "label13", db_id, table_id, t1p1, tablet_id,
&txn_id);
int64_t lock_id = txn_id;
get_delete_bitmap_update_lock(meta_service.get(), table_id, t1p1, lock_id, initiator);
set_partition_version(meta_service.get(), instance_id, db_id, table_id, t1p1,
cur_max_version, {12345});
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap(meta_service.get(), update_delete_bitmap_req, update_delete_bitmap_res,
table_id, t1p1, lock_id, initiator, tablet_id, txn_id,
cur_max_version + 2);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
}
}
TEST(MetaServiceTest, UpdateDeleteBitmapCheckPartitionVersionFail) {
auto meta_service = get_meta_service();
brpc::Controller cntl;
extern std::string get_instance_id(const std::shared_ptr<ResourceManager>& rc_mgr,
const std::string& cloud_unique_id);
auto instance_id = get_instance_id(meta_service->resource_mgr(), "test_cloud_unique_id");
{
// 2. abnormal path
// 2.1 has partition version but request version does not match
int64_t db_id = 999;
int64_t table_id = 2001;
int64_t index_id = 4001;
int64_t t1p1 = 2001;
int64_t tablet_id = 3001;
int64_t initiator = -1;
int64_t cur_max_version = 100;
int64_t txn_id;
ASSERT_NO_FATAL_FAILURE(create_tablet_with_db_id(meta_service.get(), db_id, table_id,
index_id, t1p1, tablet_id));
begin_txn_and_commit_rowset(meta_service.get(), "label21", db_id, table_id, t1p1, tablet_id,
&txn_id);
int64_t lock_id = txn_id;
get_delete_bitmap_update_lock(meta_service.get(), table_id, t1p1, lock_id, initiator);
set_partition_version(meta_service.get(), instance_id, db_id, table_id, t1p1,
cur_max_version);
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
// wrong version
update_delete_bitmap(meta_service.get(), update_delete_bitmap_req, update_delete_bitmap_res,
table_id, t1p1, lock_id, initiator, tablet_id, txn_id,
cur_max_version + 2);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::VERSION_NOT_MATCH);
}
{
// 2. abnormal path
// 2.2 does not have partition version KV and request version does not match
int64_t db_id = 999;
int64_t table_id = 2002;
int64_t index_id = 4001;
int64_t t1p1 = 2001;
int64_t tablet_id = 3001;
int64_t initiator = -1;
int64_t txn_id;
ASSERT_NO_FATAL_FAILURE(create_tablet_with_db_id(meta_service.get(), db_id, table_id,
index_id, t1p1, tablet_id));
begin_txn_and_commit_rowset(meta_service.get(), "label22", db_id, table_id, t1p1, tablet_id,
&txn_id);
int64_t lock_id = txn_id;
get_delete_bitmap_update_lock(meta_service.get(), table_id, t1p1, lock_id, initiator);
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
// first load, wrong version
update_delete_bitmap(meta_service.get(), update_delete_bitmap_req, update_delete_bitmap_res,
table_id, t1p1, lock_id, initiator, tablet_id, txn_id, 10);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::VERSION_NOT_MATCH);
}
{
// 2. abnormal path
// 2.3 has partition version and pending txn, and request version matches
int64_t db_id = 999;
int64_t table_id = 2003;
int64_t index_id = 4001;
int64_t t1p1 = 2001;
int64_t tablet_id = 3001;
int64_t initiator = -1;
int64_t cur_max_version = 120;
int64_t txn_id;
ASSERT_NO_FATAL_FAILURE(create_tablet_with_db_id(meta_service.get(), db_id, table_id,
index_id, t1p1, tablet_id));
begin_txn_and_commit_rowset(meta_service.get(), "label23", db_id, table_id, t1p1, tablet_id,
&txn_id);
int64_t lock_id = txn_id;
get_delete_bitmap_update_lock(meta_service.get(), table_id, t1p1, lock_id, initiator);
set_partition_version(meta_service.get(), instance_id, db_id, table_id, t1p1,
cur_max_version, {12345});
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
// wrong version
update_delete_bitmap(meta_service.get(), update_delete_bitmap_req, update_delete_bitmap_res,
table_id, t1p1, lock_id, initiator, tablet_id, txn_id,
cur_max_version + 1);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::VERSION_NOT_MATCH);
}
}
TEST(MetaServiceTest, UpdateDeleteBitmapFailCase) {
// simulate the situation described in https://github.com/apache/doris/pull/49710
auto meta_service = get_meta_service();
brpc::Controller cntl;
extern std::string get_instance_id(const std::shared_ptr<ResourceManager>& rc_mgr,
const std::string& cloud_unique_id);
auto instance_id = get_instance_id(meta_service->resource_mgr(), "test_cloud_unique_id");
int64_t db_id = 1999;
int64_t table_id = 1001;
int64_t index_id = 4001;
int64_t t1p1 = 2001;
int64_t tablet_id = 3001;
int64_t initiator = -1;
int64_t cur_max_version = 100;
set_partition_version(meta_service.get(), instance_id, db_id, table_id, t1p1, cur_max_version);
ASSERT_NO_FATAL_FAILURE(create_tablet_with_db_id(meta_service.get(), db_id, table_id, index_id,
t1p1, tablet_id));
// txn1 begins
int64_t txn_id1;
begin_txn_and_commit_rowset(meta_service.get(), "label31", db_id, table_id, t1p1, tablet_id,
&txn_id1);
int64_t txn1_version_to_publish = cur_max_version + 1;
// txn1 gains the lock and try to publish with version 101
int64_t lock_id = txn_id1;
get_delete_bitmap_update_lock(meta_service.get(), table_id, t1p1, lock_id, initiator);
// txn1 failed due to calculation timeout and removes the delete bitmap lock
RemoveDeleteBitmapUpdateLockRequest remove_req;
RemoveDeleteBitmapUpdateLockResponse remove_res;
remove_req.set_cloud_unique_id("test_cloud_unique_id");
remove_req.set_table_id(table_id);
remove_req.set_lock_id(lock_id);
remove_req.set_initiator(-1);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_req, &remove_res,
nullptr);
ASSERT_EQ(remove_res.status().code(), MetaServiceCode::OK);
// txn2 gains the lock and succeeds to publish with version 101
int64_t txn_id2;
begin_txn_and_commit_rowset(meta_service.get(), "label32", db_id, table_id, t1p1, tablet_id,
&txn_id2);
lock_id = txn_id2;
get_delete_bitmap_update_lock(meta_service.get(), table_id, t1p1, lock_id, initiator);
int64_t txn2_version_to_publish = cur_max_version + 1;
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
std::string data1 = "1234";
update_delete_bitmap(meta_service.get(), update_delete_bitmap_req, update_delete_bitmap_res,
table_id, t1p1, lock_id, initiator, tablet_id, txn_id2,
txn2_version_to_publish, data1);
CommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(txn_id2);
req.add_mow_table_ids(table_id);
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string ver_key = partition_version_key({instance_id, db_id, table_id, t1p1});
std::string ver_val;
VersionPB version_pb;
auto ret = txn->get(ver_key, &ver_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_OK);
ASSERT_TRUE(version_pb.ParseFromString(ver_val));
ASSERT_EQ(version_pb.version(), cur_max_version + 1);
std::string lock_key = meta_delete_bitmap_update_lock_key({instance_id, table_id, -1});
std::string lock_val;
ret = txn->get(lock_key, &lock_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_KEY_NOT_FOUND);
// txn1 retries to publish and gains the lock, try to publish with version 102
lock_id = txn_id1;
get_delete_bitmap_update_lock(meta_service.get(), table_id, t1p1, lock_id, initiator);
// txn1's previous calculation task finshes and try to update delete bitmap with version 101
std::string data2 = "5678";
update_delete_bitmap(meta_service.get(), update_delete_bitmap_req, update_delete_bitmap_res,
table_id, t1p1, lock_id, initiator, tablet_id, txn_id1,
txn1_version_to_publish, data2);
// this should fail
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::VERSION_NOT_MATCH);
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(tablet_id);
get_delete_bitmap_req.add_rowset_ids("123");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(cur_max_version + 1);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_ids_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(0), data1);
}
TEST(MetaServiceTest, UpdateDeleteBitmapScOverrideExistingKey) {
auto meta_service = get_meta_service();
brpc::Controller cntl;
size_t split_size = 90 * 1000; // see cloud/src/common/util.h
extern std::string get_instance_id(const std::shared_ptr<ResourceManager>& rc_mgr,
const std::string& cloud_unique_id);
auto instance_id = get_instance_id(meta_service->resource_mgr(), "test_cloud_unique_id");
{
// schema change should use pending delete bitmap to clear previous failed trials
int64_t db_id = 99999;
int64_t table_id = 1801;
int64_t index_id = 4801;
int64_t t1p1 = 2001;
int64_t tablet_id = 3001;
int64_t txn_id;
ASSERT_NO_FATAL_FAILURE(create_tablet_with_db_id(meta_service.get(), db_id, table_id,
index_id, t1p1, tablet_id));
begin_txn_and_commit_rowset(meta_service.get(), "label11", db_id, table_id, t1p1, tablet_id,
&txn_id);
int64_t lock_id = -2;
int64_t initiator = 1009;
int64_t version = 100;
get_delete_bitmap_update_lock(meta_service.get(), table_id, t1p1, lock_id, initiator);
{
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
// will be splited and stored in 5 KVs
std::string data1(split_size * 5, 'c');
update_delete_bitmap(meta_service.get(), update_delete_bitmap_req,
update_delete_bitmap_res, table_id, t1p1, lock_id, initiator,
tablet_id, txn_id, version, data1);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(tablet_id);
get_delete_bitmap_req.add_rowset_ids("123");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(version);
meta_service->get_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_ids_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(0), data1);
}
{
std::string pending_key = meta_pending_delete_bitmap_key({instance_id, tablet_id});
std::string pending_val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(pending_key, &pending_val), TxnErrorCode::TXN_OK);
PendingDeleteBitmapPB pending_info;
ASSERT_TRUE(pending_info.ParseFromString(pending_val));
ASSERT_EQ(pending_info.delete_bitmap_keys_size(), 1);
std::string_view k1 = pending_info.delete_bitmap_keys(0);
k1.remove_prefix(1);
std::vector<std::tuple<std::variant<int64_t, std::string>, int, int>> out;
decode_key(&k1, &out);
// 0x01 "meta" ${instance_id} "delete_bitmap" ${tablet_id} ${rowset_id} ${version} ${segment_id} -> roaringbitmap
auto encoded_tablet_id = std::get<std::int64_t>(std::get<0>(out[3]));
ASSERT_EQ(encoded_tablet_id, tablet_id);
auto encoded_rowset_id = std::get<std::string>(std::get<0>(out[4]));
ASSERT_EQ(encoded_rowset_id, "123");
auto encoded_version = std::get<std::int64_t>(std::get<0>(out[5]));
ASSERT_EQ(encoded_version, version);
auto encoded_segment_id = std::get<std::int64_t>(std::get<0>(out[6]));
ASSERT_EQ(encoded_segment_id, 0);
}
{
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
// will be splited and stored in 3 KVs
// if we don't remove previous splited KVs, will crash when reading
std::string data2(split_size * 3, 'a');
update_delete_bitmap(meta_service.get(), update_delete_bitmap_req,
update_delete_bitmap_res, table_id, t1p1, lock_id, initiator,
tablet_id, txn_id, version, data2);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(tablet_id);
get_delete_bitmap_req.add_rowset_ids("123");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(version);
meta_service->get_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_ids_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(0), data2);
}
}
}
void testUpdateDeleteBitmap(int lock_version) {
config::delete_bitmap_lock_v2_white_list = lock_version == 1 ? "" : "*";
auto meta_service = get_meta_service();
remove_delete_bitmap_lock(meta_service.get(), 112);
extern std::string get_instance_id(const std::shared_ptr<ResourceManager>& rc_mgr,
const std::string& cloud_unique_id);
auto instance_id = get_instance_id(meta_service->resource_mgr(), "test_cloud_unique_id");
// get delete bitmap update lock
brpc::Controller cntl;
GetDeleteBitmapUpdateLockRequest get_lock_req;
GetDeleteBitmapUpdateLockResponse get_lock_res;
get_lock_req.set_cloud_unique_id("test_cloud_unique_id");
get_lock_req.set_table_id(112);
get_lock_req.add_partition_ids(123);
get_lock_req.set_expiration(5);
get_lock_req.set_lock_id(888);
get_lock_req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
// first update delete bitmap
std::vector<std::string> delete_bitmap_keys;
{
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(112);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(888);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(333);
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps("abc0");
delete_bitmap_keys.emplace_back(meta_delete_bitmap_key({instance_id, 333, "123", 2, 1}));
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(3);
update_delete_bitmap_req.add_segment_delete_bitmaps("abc1");
delete_bitmap_keys.emplace_back(meta_delete_bitmap_key({instance_id, 333, "123", 3, 0}));
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(3);
update_delete_bitmap_req.add_segment_delete_bitmaps("abc2");
delete_bitmap_keys.emplace_back(meta_delete_bitmap_key({instance_id, 333, "123", 3, 1}));
update_delete_bitmap_req.add_rowset_ids("124");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps("abc3");
delete_bitmap_keys.emplace_back(meta_delete_bitmap_key({instance_id, 333, "124", 2, 0}));
std::string large_value = generate_random_string(300 * 1000);
update_delete_bitmap_req.add_rowset_ids("124");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps(large_value);
delete_bitmap_keys.emplace_back(meta_delete_bitmap_key({instance_id, 333, "124", 2, 1}));
update_delete_bitmap_req.add_rowset_ids("124");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(3);
update_delete_bitmap_req.add_segment_delete_bitmaps("abc4");
delete_bitmap_keys.emplace_back(meta_delete_bitmap_key({instance_id, 333, "124", 3, 0}));
// v2 update delete bitmap
{
DeleteBitmapPB delete_bitmap;
delete_bitmap.add_rowset_ids("123");
delete_bitmap.add_segment_ids(1);
delete_bitmap.add_versions(2);
delete_bitmap.add_segment_delete_bitmaps("abc0");
delete_bitmap.add_rowset_ids("123");
delete_bitmap.add_segment_ids(0);
delete_bitmap.add_versions(3);
delete_bitmap.add_segment_delete_bitmaps("abc1");
delete_bitmap.add_rowset_ids("123");
delete_bitmap.add_segment_ids(1);
delete_bitmap.add_versions(3);
delete_bitmap.add_segment_delete_bitmaps("abc2");
DeleteBitmapStoragePB delete_bitmap_storage_pb;
delete_bitmap_storage_pb.set_store_in_fdb(true);
*(delete_bitmap_storage_pb.mutable_delete_bitmap()) = std::move(delete_bitmap);
*(update_delete_bitmap_req.add_delete_bitmap_storages()) =
std::move(delete_bitmap_storage_pb);
update_delete_bitmap_req.add_delta_rowset_ids("124");
delete_bitmap_keys.emplace_back(
versioned::meta_delete_bitmap_key({instance_id, 333, "124"}));
}
{
DeleteBitmapPB delete_bitmap;
delete_bitmap.add_rowset_ids("124");
delete_bitmap.add_segment_ids(0);
delete_bitmap.add_versions(2);
delete_bitmap.add_segment_delete_bitmaps("abc3");
delete_bitmap.add_rowset_ids("124");
delete_bitmap.add_segment_ids(1);
delete_bitmap.add_versions(2);
delete_bitmap.add_segment_delete_bitmaps(large_value);
delete_bitmap.add_rowset_ids("124");
delete_bitmap.add_segment_ids(0);
delete_bitmap.add_versions(3);
delete_bitmap.add_segment_delete_bitmaps("abc4");
DeleteBitmapStoragePB delete_bitmap_storage_pb;
delete_bitmap_storage_pb.set_store_in_fdb(true);
*(delete_bitmap_storage_pb.mutable_delete_bitmap()) = std::move(delete_bitmap);
*(update_delete_bitmap_req.add_delete_bitmap_storages()) =
std::move(delete_bitmap_storage_pb);
update_delete_bitmap_req.add_delta_rowset_ids("125");
delete_bitmap_keys.emplace_back(
versioned::meta_delete_bitmap_key({instance_id, 333, "125"}));
}
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
// first get delete bitmap
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(333);
get_delete_bitmap_req.add_rowset_ids("123");
get_delete_bitmap_req.add_begin_versions(3);
get_delete_bitmap_req.add_end_versions(3);
get_delete_bitmap_req.add_rowset_ids("124");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(3);
get_delete_bitmap_req.add_rowset_ids("125");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(3);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
auto check_v1_delete_bitmap = [&](GetDeleteBitmapResponse get_delete_bitmap_res) {
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), 5);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 5);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), 5);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 5);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(0), "123");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(0), 0);
ASSERT_EQ(get_delete_bitmap_res.versions(0), 3);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(0), "abc1");
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(1), "123");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(1), 1);
ASSERT_EQ(get_delete_bitmap_res.versions(1), 3);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(1), "abc2");
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(2), "124");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(2), 0);
ASSERT_EQ(get_delete_bitmap_res.versions(2), 2);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(2), "abc3");
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(3), "124");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(3), 1);
ASSERT_EQ(get_delete_bitmap_res.versions(3), 2);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(3), large_value);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(4), "124");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(4), 0);
ASSERT_EQ(get_delete_bitmap_res.versions(4), 3);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(4), "abc4");
};
check_v1_delete_bitmap(get_delete_bitmap_res);
// v2 get delete bitmap
auto check_v2_delete_bitmap = [&](GetDeleteBitmapResponse& get_delete_bitmap_res2) {
ASSERT_EQ(get_delete_bitmap_res2.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res2.delete_bitmap_storages_size(), 2);
{
auto& delete_bitmap_storage = get_delete_bitmap_res2.delete_bitmap_storages(0);
ASSERT_TRUE(delete_bitmap_storage.store_in_fdb());
auto& delete_bitmap_pb = delete_bitmap_storage.delete_bitmap();
ASSERT_EQ(delete_bitmap_pb.rowset_ids_size(), 3);
ASSERT_EQ(delete_bitmap_pb.versions_size(), 3);
ASSERT_EQ(delete_bitmap_pb.segment_ids_size(), 3);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps_size(), 3);
ASSERT_EQ(delete_bitmap_pb.rowset_ids(0), "123");
ASSERT_EQ(delete_bitmap_pb.segment_ids(0), 1);
ASSERT_EQ(delete_bitmap_pb.versions(0), 2);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(0), "abc0");
ASSERT_EQ(delete_bitmap_pb.rowset_ids(1), "123");
ASSERT_EQ(delete_bitmap_pb.segment_ids(1), 0);
ASSERT_EQ(delete_bitmap_pb.versions(1), 3);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(1), "abc1");
ASSERT_EQ(delete_bitmap_pb.rowset_ids(2), "123");
ASSERT_EQ(delete_bitmap_pb.segment_ids(2), 1);
ASSERT_EQ(delete_bitmap_pb.versions(2), 3);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(2), "abc2");
}
{
auto& delete_bitmap_storage = get_delete_bitmap_res2.delete_bitmap_storages(1);
ASSERT_TRUE(delete_bitmap_storage.store_in_fdb());
auto& delete_bitmap_pb = delete_bitmap_storage.delete_bitmap();
ASSERT_EQ(delete_bitmap_pb.rowset_ids_size(), 3);
ASSERT_EQ(delete_bitmap_pb.versions_size(), 3);
ASSERT_EQ(delete_bitmap_pb.segment_ids_size(), 3);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps_size(), 3);
ASSERT_EQ(delete_bitmap_pb.rowset_ids(0), "124");
ASSERT_EQ(delete_bitmap_pb.segment_ids(0), 0);
ASSERT_EQ(delete_bitmap_pb.versions(0), 2);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(0), "abc3");
ASSERT_EQ(delete_bitmap_pb.rowset_ids(1), "124");
ASSERT_EQ(delete_bitmap_pb.segment_ids(1), 1);
ASSERT_EQ(delete_bitmap_pb.versions(1), 2);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(1), large_value);
ASSERT_EQ(delete_bitmap_pb.rowset_ids(2), "124");
ASSERT_EQ(delete_bitmap_pb.segment_ids(2), 0);
ASSERT_EQ(delete_bitmap_pb.versions(2), 3);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(2), "abc4");
}
};
{
GetDeleteBitmapResponse get_delete_bitmap_res2;
get_delete_bitmap_req.set_store_version(2);
meta_service->get_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res2, nullptr);
check_v2_delete_bitmap(get_delete_bitmap_res2);
}
// v1 and v2 get delete bitmap
{
GetDeleteBitmapResponse get_delete_bitmap_res3;
get_delete_bitmap_req.set_store_version(3);
meta_service->get_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res3, nullptr);
check_v1_delete_bitmap(get_delete_bitmap_res3);
check_v2_delete_bitmap(get_delete_bitmap_res3);
}
// check pending delete bitmap key
{
std::string pending_key = meta_pending_delete_bitmap_key({instance_id, 333});
std::string pending_val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(pending_key, &pending_val), TxnErrorCode::TXN_OK);
PendingDeleteBitmapPB pending_info;
ASSERT_TRUE(pending_info.ParseFromString(pending_val));
ASSERT_EQ(pending_info.delete_bitmap_keys_size(), delete_bitmap_keys.size());
for (size_t i = 0; i < delete_bitmap_keys.size(); ++i) {
ASSERT_EQ(pending_info.delete_bitmap_keys(i), delete_bitmap_keys[i]);
}
}
}
// second update delete bitmap
{
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(112);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(888);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(333);
std::string large_value = generate_random_string(200 * 1000);
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps(large_value);
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps("bbb0");
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(3);
update_delete_bitmap_req.add_segment_delete_bitmaps("bbb1");
update_delete_bitmap_req.add_rowset_ids("124");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(3);
update_delete_bitmap_req.add_segment_delete_bitmaps("bbb2");
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
// second get delete bitmap
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(333);
get_delete_bitmap_req.add_rowset_ids("123");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(3);
get_delete_bitmap_req.add_rowset_ids("124");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(3);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), 4);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 4);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), 4);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 4);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(0), "123");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(0), 0);
ASSERT_EQ(get_delete_bitmap_res.versions(0), 2);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(0), large_value);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(1), "123");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(1), 1);
ASSERT_EQ(get_delete_bitmap_res.versions(1), 2);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(1), "bbb0");
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(2), "123");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(2), 1);
ASSERT_EQ(get_delete_bitmap_res.versions(2), 3);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(2), "bbb1");
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(3), "124");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(3), 1);
ASSERT_EQ(get_delete_bitmap_res.versions(3), 3);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(3), "bbb2");
}
// large size txn
{
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(112);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(888);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(333);
std::string large_value = generate_random_string(300 * 1000);
for (size_t i = 0; i < 100; ++i) {
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(i);
update_delete_bitmap_req.add_segment_delete_bitmaps(large_value);
}
update_delete_bitmap_req.add_rowset_ids("124");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(3);
update_delete_bitmap_req.add_segment_delete_bitmaps("abcd4");
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(333);
get_delete_bitmap_req.add_rowset_ids("123");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(101);
get_delete_bitmap_req.add_rowset_ids("124");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(3);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), 101);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 101);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), 101);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 101);
for (size_t i = 0; i < 100; ++i) {
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(i), "123");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(i), 1);
ASSERT_EQ(get_delete_bitmap_res.versions(i), i);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(i), large_value);
}
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(100), "124");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(100), 0);
ASSERT_EQ(get_delete_bitmap_res.versions(100), 3);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(100), "abcd4");
}
// update existing delete bitmap key
{
//first update new key
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(112);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(888);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(333);
std::string large_value = generate_random_string(300 * 1000 * 3);
update_delete_bitmap_req.add_rowset_ids("456");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps(large_value);
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(333);
get_delete_bitmap_req.add_rowset_ids("456");
get_delete_bitmap_req.add_begin_versions(2);
get_delete_bitmap_req.add_end_versions(2);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(0), "456");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(0), 0);
ASSERT_EQ(get_delete_bitmap_res.versions(0), 2);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(0), large_value);
}
RemoveDeleteBitmapUpdateLockRequest remove_lock_req;
RemoveDeleteBitmapUpdateLockResponse remove_lock_res;
remove_lock_req.set_cloud_unique_id("test_cloud_unique_id");
remove_lock_req.set_table_id(112);
remove_lock_req.set_lock_id(888);
remove_lock_req.set_initiator(-1);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_lock_req,
&remove_lock_res, nullptr);
ASSERT_EQ(remove_lock_res.status().code(), MetaServiceCode::OK);
for (int i = 0; i < 2; ++i) {
auto lock_id =
i == 0 ? COMPACTION_DELETE_BITMAP_LOCK_ID : SCHEMA_CHANGE_DELETE_BITMAP_LOCK_ID;
// case: compaction or schema_change update delete bitmap
get_lock_req.set_lock_id(lock_id);
get_lock_req.set_initiator(800);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
// update delete bitmap
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(112);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(lock_id);
update_delete_bitmap_req.set_initiator(800);
update_delete_bitmap_req.set_tablet_id(333);
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps("compaction0");
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
// remove lock
remove_lock_req.set_lock_id(lock_id);
remove_lock_req.set_initiator(800);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_lock_req,
&remove_lock_res, nullptr);
ASSERT_EQ(remove_lock_res.status().code(), MetaServiceCode::OK);
// case: compaction or schema_change update delete bitmap with lock expired
get_lock_req.set_lock_id(lock_id);
get_lock_req.set_initiator(800);
get_lock_req.set_expiration(1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
// load get lock
sleep(2);
get_lock_req.set_lock_id(100);
get_lock_req.set_initiator(-1);
get_lock_req.set_expiration(1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
// compaction update delete bitmap
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::LOCK_EXPIRED);
// case: compaction2 or schema_change2 get lock
sleep(2);
get_lock_req.set_lock_id(lock_id);
get_lock_req.set_initiator(810);
get_lock_req.set_expiration(1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
// compaction1 update delete bitmap
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::LOCK_EXPIRED);
// remove compaction2 or or schema_change2 lock
remove_lock_req.set_lock_id(lock_id);
remove_lock_req.set_initiator(810);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_lock_req,
&remove_lock_res, nullptr);
ASSERT_EQ(remove_lock_res.status().code(), MetaServiceCode::OK);
}
{
//compaction update delete bitmap without lock
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(112);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_without_lock(true);
update_delete_bitmap_req.set_lock_id(-3);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(333);
std::string large_value = generate_random_string(300 * 1000);
update_delete_bitmap_req.add_rowset_ids("456");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps(large_value);
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(333);
get_delete_bitmap_req.add_rowset_ids("456");
get_delete_bitmap_req.add_begin_versions(2);
get_delete_bitmap_req.add_end_versions(2);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(0), "456");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(0), 0);
ASSERT_EQ(get_delete_bitmap_res.versions(0), 2);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(0), large_value);
}
remove_delete_bitmap_lock(meta_service.get(), 112);
}
TEST(MetaServiceTest, UpdateDeleteBitmap) {
testUpdateDeleteBitmap(2);
testUpdateDeleteBitmap(1);
}
TEST(MetaServiceTest, UpdateDeleteBitmapWithException) {
auto meta_service = get_meta_service();
brpc::Controller cntl;
{
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(112);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(888);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(333);
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps("abc0");
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::LOCK_EXPIRED);
}
// get delete bitmap update lock
GetDeleteBitmapUpdateLockRequest get_lock_req;
GetDeleteBitmapUpdateLockResponse get_lock_res;
get_lock_req.set_cloud_unique_id("test_cloud_unique_id");
get_lock_req.set_table_id(112);
get_lock_req.add_partition_ids(123);
get_lock_req.set_expiration(5);
get_lock_req.set_lock_id(888);
get_lock_req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
{
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(112);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(222);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(333);
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps("abc0");
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::LOCK_EXPIRED);
}
{
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(112);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(888);
update_delete_bitmap_req.set_initiator(-2);
update_delete_bitmap_req.set_tablet_id(333);
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps("abc0");
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::LOCK_EXPIRED);
}
}
void update_delete_bitmap_with_remove_pre(MetaServiceProxy* meta_service, int64_t table_id,
int64_t tablet_id, bool inject = false,
bool rowset_non_exist = false) {
// create rowset, if `rowset_non_exist` enabled, only r4 exists
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
if (rowset_non_exist) {
std::string rs_key, rs_val;
doris::RowsetMetaCloudPB rs;
rs.set_rowset_id(0);
rs_key = meta_rowset_key({"test_instance", tablet_id, 3});
rs.set_rowset_id_v2("r2-3");
ASSERT_TRUE(rs.SerializeToString(&rs_val));
txn->put(rs_key, rs_val);
rs_key = meta_rowset_key({"test_instance", tablet_id, 4});
rs.set_rowset_id_v2("r4");
ASSERT_TRUE(rs.SerializeToString(&rs_val));
txn->put(rs_key, rs_val);
} else {
for (int i = 2; i <= 4; i++) {
std::string rs_key, rs_val;
MetaRowsetKeyInfo rs_key_info {"test_instance", tablet_id, i};
meta_rowset_key(rs_key_info, &rs_key);
doris::RowsetMetaCloudPB rs;
rs.set_rowset_id(0);
rs.set_rowset_id_v2("r" + std::to_string(i));
ASSERT_TRUE(rs.SerializeToString(&rs_val));
txn->put(rs_key, rs_val);
}
}
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
}
brpc::Controller cntl;
// compaction update delete bitmap with remove pre rowset delete bitmaps
// get update lock
GetDeleteBitmapUpdateLockRequest get_lock_req;
GetDeleteBitmapUpdateLockResponse get_lock_res;
get_lock_req.set_cloud_unique_id("test_cloud_unique_id");
get_lock_req.set_table_id(table_id);
get_lock_req.set_lock_id(-1);
get_lock_req.set_initiator(203);
get_lock_req.set_expiration(10);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
// write delete bitmap
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(table_id);
update_delete_bitmap_req.set_partition_id(201);
update_delete_bitmap_req.set_tablet_id(tablet_id);
update_delete_bitmap_req.set_lock_id(-1);
update_delete_bitmap_req.set_initiator(203);
std::string large_value = generate_random_string(300 * 1000);
std::vector<std::tuple<std::string, int64_t, int64_t>> rowset_segment_version_vector = {
/* r2-0 */ {"r2", 0, 3}, {"r2", 0, 4}, {"r2", 0, 5}, {"r2", 0, 6},
/* r3-0 */ {"r3", 0, 4}, {"r3", 0, 5}, {"r3", 0, 6},
/* r3-1 */ {"r3", 1, 4}, {"r3", 1, 5},
/* r3-2 */ {"r3", 2, 4}, {"r3", 2, 6},
/* r4-0 */ {"r4", 0, 5}, {"r4", 0, 6}};
for (const auto& [rowset, segment, version] : rowset_segment_version_vector) {
update_delete_bitmap_req.add_rowset_ids(rowset);
update_delete_bitmap_req.add_segment_ids(segment);
update_delete_bitmap_req.add_versions(version);
update_delete_bitmap_req.add_segment_delete_bitmaps(large_value);
}
meta_service->update_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res,
nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
// remove delete bitmap lock
RemoveDeleteBitmapUpdateLockRequest remove_lock_req;
RemoveDeleteBitmapUpdateLockResponse remove_lock_res;
remove_lock_req.set_cloud_unique_id("test_cloud_unique_id");
remove_lock_req.set_table_id(table_id);
remove_lock_req.set_lock_id(-1);
remove_lock_req.set_initiator(203);
meta_service->remove_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &remove_lock_req,
&remove_lock_res, nullptr);
ASSERT_EQ(remove_lock_res.status().code(), MetaServiceCode::OK);
// get delete bitmap
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(tablet_id);
std::vector<std::string> rowset_vector = {"r2", "r3", "r4"};
for (const auto& rowset : rowset_vector) {
get_delete_bitmap_req.add_rowset_ids(rowset);
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(6);
}
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
auto size = rowset_segment_version_vector.size();
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), size);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), size);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), size);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), size);
// update pre rowset delete bitmap
update_delete_bitmap_req.clear_rowset_ids();
update_delete_bitmap_req.clear_segment_ids();
update_delete_bitmap_req.clear_versions();
update_delete_bitmap_req.clear_segment_delete_bitmaps();
update_delete_bitmap_req.set_lock_id(-3);
update_delete_bitmap_req.set_without_lock(true);
update_delete_bitmap_req.set_initiator(tablet_id);
update_delete_bitmap_req.set_pre_rowset_agg_start_version(4);
update_delete_bitmap_req.set_pre_rowset_agg_end_version(6);
std::vector<std::tuple<std::string, int64_t, int64_t, int64_t>>
new_rowset_segment_version_vector = {/* r2-0 */ {"r2", 0, 6, 2},
/* r3-0 */ {"r3", 0, 6, 3},
/* r3-1 */ {"r3", 1, 6, 3},
/* r3-2 */ {"r3", 2, 6, 3},
/* r4-0 */ {"r4", 0, 6, 4}};
std::string new_large_value = generate_random_string(300 * 1000);
for (const auto& [rowset, segment, version, rowset_version] :
new_rowset_segment_version_vector) {
update_delete_bitmap_req.add_rowset_ids(rowset);
update_delete_bitmap_req.add_segment_ids(segment);
update_delete_bitmap_req.add_versions(version);
update_delete_bitmap_req.add_segment_delete_bitmaps(new_large_value);
update_delete_bitmap_req.add_pre_rowset_versions(rowset_version);
}
meta_service->update_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res,
nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(),
inject ? MetaServiceCode::KV_TXN_CONFLICT : MetaServiceCode::OK);
// get delete bitmap again
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
size = 6;
if (inject) {
size = 13;
} else if (rowset_non_exist) {
size = 12;
}
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), size);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), size);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), size);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), size);
std::vector<std::tuple<std::string, int64_t, int64_t, std::string>> expected_dm;
if (inject) {
expected_dm = {/* r2-0 */ {"r2", 0, 3, large_value},
{"r2", 0, 4, large_value},
{"r2", 0, 5, large_value},
{"r2", 0, 6, new_large_value},
/* r3-0 is agg */ {"r3", 0, 4, large_value},
{"r3", 1, 4, large_value},
{"r3", 2, 4, large_value},
{"r3", 0, 5, large_value},
{"r3", 1, 5, large_value},
{"r3", 0, 6, new_large_value},
{"r3", 2, 6, large_value},
/* r4-0 */ {"r4", 0, 5, large_value},
{"r4", 0, 6, large_value}};
} else if (rowset_non_exist) {
expected_dm = {/* r2-0 */ {"r2", 0, 3, large_value},
{"r2", 0, 4, large_value},
{"r2", 0, 5, large_value},
{"r2", 0, 6, large_value},
/* r3-0 */ {"r3", 0, 4, large_value},
{"r3", 1, 4, large_value},
{"r3", 2, 4, large_value},
{"r3", 0, 5, large_value},
{"r3", 1, 5, large_value},
{"r3", 0, 6, large_value},
{"r3", 2, 6, large_value},
/* r4-0 */ {"r4", 0, 6, new_large_value}};
} else {
expected_dm = {/* r2-0 */ {"r2", 0, 3, large_value},
{"r2", 0, 6, new_large_value},
/* r3-0 */ {"r3", 0, 6, new_large_value},
/* r3-1 */ {"r3", 1, 6, new_large_value},
/* r3-2 */ {"r3", 2, 6, new_large_value},
/* r4-0 */ {"r4", 0, 6, new_large_value}};
}
for (size_t i = 0; i < get_delete_bitmap_res.rowset_ids_size(); i++) {
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(i), std::get<0>(expected_dm[i]));
ASSERT_EQ(get_delete_bitmap_res.segment_ids(i), std::get<1>(expected_dm[i]));
ASSERT_EQ(get_delete_bitmap_res.versions(i), std::get<2>(expected_dm[i]));
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(i), std::get<3>(expected_dm[i]));
}
}
TEST(MetaServiceTest, UpdateDeleteBitmapWithRemovePreDeleteBitmap) {
auto meta_service = get_meta_service();
[[maybe_unused]] auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
update_delete_bitmap_with_remove_pre(meta_service.get(), 200, 202);
int64_t max_txn_commit_byte = config::max_txn_commit_byte;
config::max_txn_commit_byte = 1000;
update_delete_bitmap_with_remove_pre(meta_service.get(), 300, 302);
sp->set_call_back("update_delete_bitmap:commit:err", [&](auto&& args) {
auto initiator = try_any_cast<int64_t>(args[0]);
auto i = try_any_cast<size_t>(args[1]);
if (initiator == 402 && i == 2) {
*try_any_cast<TxnErrorCode*>(args[2]) = TxnErrorCode::TXN_CONFLICT;
}
});
sp->enable_processing();
update_delete_bitmap_with_remove_pre(meta_service.get(), 400, 402, true);
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
config::max_txn_commit_byte = max_txn_commit_byte;
update_delete_bitmap_with_remove_pre(meta_service.get(), 500, 502, false, true);
}
TEST(MetaServiceTest, GetDeleteBitmapWithIdx) {
auto meta_service = get_meta_service();
extern std::string get_instance_id(const std::shared_ptr<ResourceManager>& rc_mgr,
const std::string& cloud_unique_id);
auto instance_id = get_instance_id(meta_service->resource_mgr(), "test_cloud_unique_id");
int64_t db_id = 1;
int64_t table_id = 1;
int64_t index_id = 1;
int64_t partition_id = 1;
int64_t tablet_id = 123;
brpc::Controller cntl;
GetDeleteBitmapRequest req;
GetDeleteBitmapResponse res;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_tablet_id(tablet_id);
TabletIndexPB idx;
idx.set_tablet_id(tablet_id);
idx.set_index_id(index_id);
idx.set_db_id(db_id);
idx.set_partition_id(partition_id);
idx.set_table_id(table_id);
*(req.mutable_idx()) = idx;
req.set_base_compaction_cnt(9);
req.set_cumulative_compaction_cnt(19);
req.set_cumulative_point(21);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
EXPECT_EQ(res.status().code(), MetaServiceCode::TABLET_NOT_FOUND);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string stats_key =
stats_tablet_key({instance_id, table_id, index_id, partition_id, tablet_id});
TabletStatsPB stats;
stats.set_base_compaction_cnt(9);
stats.set_cumulative_compaction_cnt(19);
stats.set_cumulative_point(20);
txn->put(stats_key, stats.SerializeAsString());
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
EXPECT_EQ(res.status().code(), MetaServiceCode::ROWSETS_EXPIRED);
req.set_cumulative_point(20);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
EXPECT_EQ(res.status().code(), MetaServiceCode::OK);
req.add_rowset_ids("1234");
req.add_begin_versions(1);
req.add_end_versions(2);
req.add_end_versions(3);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
EXPECT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
TEST(MetaServiceTest, DeleteBimapCommitTxnTest) {
auto meta_service = get_meta_service();
extern std::string get_instance_id(const std::shared_ptr<ResourceManager>& rc_mgr,
const std::string& cloud_unique_id);
auto instance_id = get_instance_id(meta_service->resource_mgr(), "test_cloud_unique_id");
// case: first version of rowset
{
int64_t txn_id = 98765;
int64_t table_id = 123456; // same as table_id of tmp rowset
int64_t db_id = 222;
int64_t tablet_id_base = 8113;
int64_t partition_id = 1234;
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(db_id);
txn_info_pb.set_label("test_label");
txn_info_pb.add_table_ids(table_id);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// mock rowset and tablet
for (int i = 0; i < 5; ++i) {
create_tablet(meta_service.get(), table_id, 1235, partition_id, tablet_id_base + i);
auto tmp_rowset = create_rowset(txn_id, tablet_id_base + i);
tmp_rowset.set_partition_id(partition_id);
CreateRowsetResponse res;
commit_rowset(meta_service.get(), tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// update delete bitmap
{
// get delete bitmap update lock
brpc::Controller cntl;
GetDeleteBitmapUpdateLockRequest get_lock_req;
GetDeleteBitmapUpdateLockResponse get_lock_res;
get_lock_req.set_cloud_unique_id("test_cloud_unique_id");
get_lock_req.set_table_id(table_id);
get_lock_req.add_partition_ids(partition_id);
get_lock_req.set_expiration(5);
get_lock_req.set_lock_id(txn_id);
get_lock_req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
// first update delete bitmap
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(table_id);
update_delete_bitmap_req.set_partition_id(partition_id);
update_delete_bitmap_req.set_lock_id(txn_id);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(tablet_id_base);
update_delete_bitmap_req.add_rowset_ids("123");
update_delete_bitmap_req.add_segment_ids(1);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps("abc0");
meta_service->update_delete_bitmap(
reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res, nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
}
// check delete bitmap update lock and pending delete bitmap
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string lock_key = meta_delete_bitmap_update_lock_key({instance_id, table_id, -1});
std::string lock_val;
auto ret = txn->get(lock_key, &lock_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_OK);
DeleteBitmapUpdateLockPB lock_info;
ASSERT_TRUE(lock_info.ParseFromString(lock_val));
std::string pending_key = meta_pending_delete_bitmap_key({instance_id, tablet_id_base});
std::string pending_val;
ret = txn->get(pending_key, &pending_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_OK);
PendingDeleteBitmapPB pending_info;
ASSERT_TRUE(pending_info.ParseFromString(pending_val));
}
// commit txn
{
brpc::Controller cntl;
CommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(db_id);
req.set_txn_id(txn_id);
req.add_mow_table_ids(table_id);
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// check delete bitmap update lock and pending delete bitmap
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string lock_key = meta_delete_bitmap_update_lock_key({instance_id, table_id, -1});
std::string lock_val;
auto ret = txn->get(lock_key, &lock_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_KEY_NOT_FOUND);
std::string pending_key = meta_pending_delete_bitmap_key({instance_id, tablet_id_base});
std::string pending_val;
ret = txn->get(pending_key, &pending_val);
ASSERT_EQ(ret, TxnErrorCode::TXN_KEY_NOT_FOUND);
}
}
}
TEST(MetaServiceTest, GetDeleteBitmapWithRetryTest1) {
auto meta_service = get_meta_service();
SyncPoint::get_instance()->enable_processing();
size_t index = 0;
SyncPoint::get_instance()->set_call_back("get_delete_bitmap_code", [&](auto&& args) {
LOG(INFO) << "GET_DELETE_BITMAP_CODE,index=" << index;
if (++index < 2) {
*doris::try_any_cast<MetaServiceCode*>(args[0]) = MetaServiceCode::KV_TXN_TOO_OLD;
}
});
// get delete bitmap update lock
brpc::Controller cntl;
GetDeleteBitmapUpdateLockRequest get_lock_req;
GetDeleteBitmapUpdateLockResponse get_lock_res;
get_lock_req.set_cloud_unique_id("test_cloud_unique_id");
get_lock_req.set_table_id(100);
get_lock_req.add_partition_ids(123);
get_lock_req.set_expiration(5);
get_lock_req.set_lock_id(888);
get_lock_req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
//first update new key
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(100);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(888);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(333);
std::string large_value = generate_random_string(300 * 1000 * 3);
update_delete_bitmap_req.add_rowset_ids("456");
update_delete_bitmap_req.add_segment_ids(0);
update_delete_bitmap_req.add_versions(2);
update_delete_bitmap_req.add_segment_delete_bitmaps(large_value);
meta_service->update_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res,
nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(333);
get_delete_bitmap_req.add_rowset_ids("456");
get_delete_bitmap_req.add_begin_versions(2);
get_delete_bitmap_req.add_end_versions(2);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_ids_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), 1);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(0), "456");
ASSERT_EQ(get_delete_bitmap_res.segment_ids(0), 0);
ASSERT_EQ(get_delete_bitmap_res.versions(0), 2);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(0), large_value);
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, GetDeleteBitmapWithRetryTest2) {
auto meta_service = get_meta_service();
SyncPoint::get_instance()->enable_processing();
size_t index = 0;
int store_version = 1;
SyncPoint::get_instance()->set_call_back("get_delete_bitmap_test", [&](auto&& args) {
auto* test = try_any_cast<bool*>(args[0]);
*test = true;
LOG(INFO) << "GET_DELETE_BITMAP_TEST, test=" << *test;
});
SyncPoint::get_instance()->set_call_back("get_delete_bitmap_err", [&](auto&& args) {
auto* round = try_any_cast<int64_t*>(args[0]);
LOG(INFO) << "GET_DELETE_BITMAP_CODE,index=" << index << ",round=" << *round;
if (*round > 2 && ++index < 2) {
*try_any_cast<TxnErrorCode*>(args[1]) = TxnErrorCode::TXN_TOO_OLD;
}
if (store_version == 2 && *round < 3) {
*try_any_cast<TxnErrorCode*>(args[1]) = TxnErrorCode::TXN_TOO_OLD;
}
});
// get delete bitmap update lock
brpc::Controller cntl;
GetDeleteBitmapUpdateLockRequest get_lock_req;
GetDeleteBitmapUpdateLockResponse get_lock_res;
get_lock_req.set_cloud_unique_id("test_cloud_unique_id");
get_lock_req.set_table_id(100);
get_lock_req.add_partition_ids(123);
get_lock_req.set_expiration(5);
get_lock_req.set_lock_id(888);
get_lock_req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
//first update new key
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(100);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(888);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(333);
std::string rowset_id = "456";
std::string segment_delete_bitmaps[5];
for (int i = 0; i < 5; i++) {
segment_delete_bitmaps[i] = generate_random_string(300 * 1000 * 3);
}
int count = 5;
DeleteBitmapPB delete_bitmap;
for (int i = 0; i < count; i++) {
update_delete_bitmap_req.add_rowset_ids(rowset_id);
update_delete_bitmap_req.add_segment_ids(i);
update_delete_bitmap_req.add_versions(i + 1);
update_delete_bitmap_req.add_segment_delete_bitmaps(segment_delete_bitmaps[i]);
delete_bitmap.add_rowset_ids(rowset_id);
delete_bitmap.add_segment_ids(i);
delete_bitmap.add_versions(i + 1);
delete_bitmap.add_segment_delete_bitmaps(segment_delete_bitmaps[i]);
}
// v2 update delete bitmap
DeleteBitmapStoragePB delete_bitmap_storage_pb;
delete_bitmap_storage_pb.set_store_in_fdb(true);
*(delete_bitmap_storage_pb.mutable_delete_bitmap()) = std::move(delete_bitmap);
*(update_delete_bitmap_req.add_delete_bitmap_storages()) = std::move(delete_bitmap_storage_pb);
update_delete_bitmap_req.add_delta_rowset_ids(rowset_id);
meta_service->update_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res,
nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
// v1 get delete bitmap
GetDeleteBitmapRequest get_delete_bitmap_req;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(333);
// add a rowset which does not exist
get_delete_bitmap_req.add_rowset_ids(rowset_id + "_non");
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(count);
get_delete_bitmap_req.add_rowset_ids(rowset_id);
get_delete_bitmap_req.add_begin_versions(0);
get_delete_bitmap_req.add_end_versions(count);
{
GetDeleteBitmapResponse get_delete_bitmap_res;
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), count);
ASSERT_EQ(get_delete_bitmap_res.segment_ids_size(), count);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), count);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), count);
for (int i = 0; i < count; i++) {
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(i), rowset_id);
ASSERT_EQ(get_delete_bitmap_res.segment_ids(i), i);
ASSERT_EQ(get_delete_bitmap_res.versions(i), i + 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(i), segment_delete_bitmaps[i]);
}
}
// v2 get delete bitmap
{
store_version = 2;
get_delete_bitmap_req.set_store_version(2);
GetDeleteBitmapResponse get_delete_bitmap_res;
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.delete_bitmap_storages_size(), 1);
auto& delete_bitmap_storage = get_delete_bitmap_res.delete_bitmap_storages(0);
ASSERT_TRUE(delete_bitmap_storage.store_in_fdb());
auto& delete_bitmap_pb = delete_bitmap_storage.delete_bitmap();
ASSERT_EQ(delete_bitmap_pb.rowset_ids_size(), count);
ASSERT_EQ(delete_bitmap_pb.segment_ids_size(), count);
ASSERT_EQ(delete_bitmap_pb.versions_size(), count);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps_size(), count);
for (int i = 0; i < count; i++) {
ASSERT_EQ(delete_bitmap_pb.rowset_ids(i), rowset_id);
ASSERT_EQ(delete_bitmap_pb.segment_ids(i), i);
ASSERT_EQ(delete_bitmap_pb.versions(i), i + 1);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(i), segment_delete_bitmaps[i]);
}
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, GetDeleteBitmapWithRetryTest3) {
auto meta_service = get_meta_service();
SyncPoint::get_instance()->enable_processing();
size_t index = 0;
SyncPoint::get_instance()->set_call_back("get_delete_bitmap_err", [&](auto&& args) {
auto* round = try_any_cast<int64_t*>(args[0]);
LOG(INFO) << "GET_DELETE_BITMAP_CODE,index=" << index << ",round=" << *round;
if (*round > 2 && ++index < 2) {
*try_any_cast<TxnErrorCode*>(args[1]) = TxnErrorCode::TXN_TOO_OLD;
}
});
// get delete bitmap update lock
brpc::Controller cntl;
GetDeleteBitmapUpdateLockRequest get_lock_req;
GetDeleteBitmapUpdateLockResponse get_lock_res;
get_lock_req.set_cloud_unique_id("test_cloud_unique_id");
get_lock_req.set_table_id(100);
get_lock_req.add_partition_ids(123);
get_lock_req.set_expiration(5);
get_lock_req.set_lock_id(888);
get_lock_req.set_initiator(-1);
meta_service->get_delete_bitmap_update_lock(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &get_lock_req,
&get_lock_res, nullptr);
ASSERT_EQ(get_lock_res.status().code(), MetaServiceCode::OK);
//first update new key
UpdateDeleteBitmapRequest update_delete_bitmap_req;
UpdateDeleteBitmapResponse update_delete_bitmap_res;
update_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
update_delete_bitmap_req.set_table_id(100);
update_delete_bitmap_req.set_partition_id(123);
update_delete_bitmap_req.set_lock_id(888);
update_delete_bitmap_req.set_initiator(-1);
update_delete_bitmap_req.set_tablet_id(333);
std::string rowset_id = "456";
std::string segment_delete_bitmaps[5];
for (int i = 0; i < 5; i++) {
segment_delete_bitmaps[i] = generate_random_string(300 * 1000 * 3);
}
int count = 5;
for (int i = 0; i < count; i++) {
update_delete_bitmap_req.add_rowset_ids(rowset_id);
update_delete_bitmap_req.add_segment_ids(i);
update_delete_bitmap_req.add_versions(i + 1);
update_delete_bitmap_req.add_segment_delete_bitmaps(segment_delete_bitmaps[i]);
}
meta_service->update_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&update_delete_bitmap_req, &update_delete_bitmap_res,
nullptr);
ASSERT_EQ(update_delete_bitmap_res.status().code(), MetaServiceCode::OK);
GetDeleteBitmapRequest get_delete_bitmap_req;
GetDeleteBitmapResponse get_delete_bitmap_res;
get_delete_bitmap_req.set_cloud_unique_id("test_cloud_unique_id");
get_delete_bitmap_req.set_tablet_id(333);
get_delete_bitmap_req.add_rowset_ids(rowset_id);
get_delete_bitmap_req.add_begin_versions(1);
get_delete_bitmap_req.add_end_versions(count);
meta_service->get_delete_bitmap(reinterpret_cast<google::protobuf::RpcController*>(&cntl),
&get_delete_bitmap_req, &get_delete_bitmap_res, nullptr);
ASSERT_EQ(get_delete_bitmap_res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(get_delete_bitmap_res.rowset_ids_size(), count);
ASSERT_EQ(get_delete_bitmap_res.segment_ids_size(), count);
ASSERT_EQ(get_delete_bitmap_res.versions_size(), count);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps_size(), count);
for (int i = 0; i < count; i++) {
ASSERT_EQ(get_delete_bitmap_res.rowset_ids(i), rowset_id);
ASSERT_EQ(get_delete_bitmap_res.segment_ids(i), i);
ASSERT_EQ(get_delete_bitmap_res.versions(i), i + 1);
ASSERT_EQ(get_delete_bitmap_res.segment_delete_bitmaps(i), segment_delete_bitmaps[i]);
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, GetVersion) {
auto service = get_meta_service();
int64_t table_id = 1;
int64_t partition_id = 1;
int64_t tablet_id = 1;
// INVALID_ARGUMENT
{
brpc::Controller ctrl;
GetVersionRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_table_id(table_id);
req.set_partition_id(partition_id);
GetVersionResponse resp;
service->get_version(&ctrl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::INVALID_ARGUMENT)
<< " status is " << resp.status().DebugString();
}
{
brpc::Controller ctrl;
GetVersionRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(1);
req.set_table_id(table_id);
req.set_partition_id(partition_id);
GetVersionResponse resp;
service->get_version(&ctrl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::VERSION_NOT_FOUND)
<< " status is " << resp.status().DebugString();
}
create_tablet(service.get(), table_id, 1, partition_id, tablet_id);
insert_rowset(service.get(), 1, "get_version_label_1", table_id, partition_id, tablet_id);
{
brpc::Controller ctrl;
GetVersionRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(1);
req.set_table_id(table_id);
req.set_partition_id(partition_id);
GetVersionResponse resp;
service->get_version(&ctrl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::OK)
<< " status is " << resp.status().DebugString();
ASSERT_EQ(resp.version(), 2);
}
}
TEST(MetaServiceTest, BatchGetVersion) {
struct TestCase {
std::vector<int64_t> table_ids;
std::vector<int64_t> partition_ids;
std::vector<int64_t> expected_versions;
std::vector<
std::tuple<int64_t /*table_id*/, int64_t /*partition_id*/, int64_t /*tablet_id*/>>
insert_rowsets;
};
// table ids: 2, 3, 4, 5
// partition ids: 6, 7, 8, 9
std::vector<TestCase> cases = {
// all version are missing
{{1, 2, 3, 4}, {6, 7, 8, 9}, {-1, -1, -1, -1}, {}},
// update table 1, partition 6
{{1, 2, 3, 4}, {6, 7, 8, 9}, {2, -1, -1, -1}, {{1, 6, 1}}},
// update table 2, partition 6
// update table 3, partition 7
{{1, 2, 3, 4}, {6, 7, 8, 9}, {2, -1, 2, 2}, {{3, 8, 3}, {4, 9, 4}}},
// update table 1, partition 7 twice
{{1, 2, 3, 4}, {6, 7, 8, 9}, {2, 3, 2, 2}, {{2, 7, 2}, {2, 7, 2}}},
};
auto service = get_meta_service();
create_tablet(service.get(), 1, 1, 6, 1);
create_tablet(service.get(), 2, 1, 7, 2);
create_tablet(service.get(), 3, 1, 8, 3);
create_tablet(service.get(), 4, 1, 9, 4);
size_t num_cases = cases.size();
size_t label_index = 0;
for (size_t i = 0; i < num_cases; ++i) {
auto& [table_ids, partition_ids, expected_versions, insert_rowsets] = cases[i];
for (auto [table_id, partition_id, tablet_id] : insert_rowsets) {
LOG(INFO) << "insert rowset for table " << table_id << " partition " << partition_id
<< " tablet_id " << tablet_id;
insert_rowset(service.get(), 1, std::to_string(++label_index), table_id, partition_id,
tablet_id);
}
brpc::Controller ctrl;
GetVersionRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(-1);
req.set_table_id(-1);
req.set_partition_id(-1);
req.set_batch_mode(true);
for (size_t i = 0; i < table_ids.size(); ++i) req.add_db_ids(1);
std::copy(table_ids.begin(), table_ids.end(),
google::protobuf::RepeatedFieldBackInserter(req.mutable_table_ids()));
std::copy(partition_ids.begin(), partition_ids.end(),
google::protobuf::RepeatedFieldBackInserter(req.mutable_partition_ids()));
GetVersionResponse resp;
service->get_version(&ctrl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::OK)
<< "case " << i << " status is " << resp.status().msg()
<< ", code=" << resp.status().code();
std::vector<int64_t> versions(resp.versions().begin(), resp.versions().end());
EXPECT_EQ(versions, expected_versions) << "case " << i;
}
// INVALID_ARGUMENT
{
brpc::Controller ctrl;
GetVersionRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_batch_mode(true);
GetVersionResponse resp;
service->get_version(&ctrl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::INVALID_ARGUMENT)
<< " status is " << resp.status().msg() << ", code=" << resp.status().code();
}
}
TEST(MetaServiceTest, BatchGetVersionFallback) {
constexpr size_t N = 100;
size_t i = 0;
auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("batch_get_version_err", [&](auto&& args) {
if (i++ == N / 10) {
*try_any_cast<TxnErrorCode*>(args) = TxnErrorCode::TXN_TOO_OLD;
}
});
sp->enable_processing();
auto service = get_meta_service();
for (int64_t i = 1; i <= N; ++i) {
create_tablet(service.get(), 1, 1, i, i);
insert_rowset(service.get(), 1, std::to_string(i), 1, i, i);
}
brpc::Controller ctrl;
GetVersionRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(-1);
req.set_table_id(-1);
req.set_partition_id(-1);
req.set_batch_mode(true);
for (size_t i = 1; i <= N; ++i) {
req.add_db_ids(1);
req.add_table_ids(1);
req.add_partition_ids(i);
}
GetVersionResponse resp;
service->get_version(&ctrl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::OK)
<< "case " << i << " status is " << resp.status().msg()
<< ", code=" << resp.status().code();
ASSERT_EQ(resp.versions_size(), N);
}
extern bool is_dropped_tablet(Transaction* txn, const std::string& instance_id, int64_t index_id,
int64_t partition_id);
TEST(MetaServiceTest, IsDroppedTablet) {
auto meta_service = get_meta_service();
std::string instance_id = "IsDroppedTablet";
auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->enable_processing();
meta_service = get_meta_service();
auto reset_meta_service = [&meta_service] { meta_service = get_meta_service(); };
constexpr int64_t index_id = 10002;
constexpr int64_t partition_id = 10003;
std::unique_ptr<Transaction> txn;
RecycleIndexPB index_pb;
auto index_key = recycle_index_key({instance_id, index_id});
RecyclePartitionPB partition_pb;
auto partition_key = recycle_partition_key({instance_id, partition_id});
std::string val;
// No recycle index and partition kv
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
EXPECT_FALSE(is_dropped_tablet(txn.get(), instance_id, index_id, partition_id));
// Tablet in PREPARED index
index_pb.set_state(RecycleIndexPB::PREPARED);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
EXPECT_FALSE(is_dropped_tablet(txn.get(), instance_id, index_id, partition_id));
// Tablet in DROPPED/RECYCLING index
reset_meta_service();
index_pb.set_state(RecycleIndexPB::DROPPED);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
EXPECT_TRUE(is_dropped_tablet(txn.get(), instance_id, index_id, partition_id));
reset_meta_service();
index_pb.set_state(RecycleIndexPB::RECYCLING);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
EXPECT_TRUE(is_dropped_tablet(txn.get(), instance_id, index_id, partition_id));
// Tablet in PREPARED partition
reset_meta_service();
partition_pb.set_state(RecyclePartitionPB::PREPARED);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
EXPECT_FALSE(is_dropped_tablet(txn.get(), instance_id, index_id, partition_id));
// Tablet in DROPPED/RECYCLING partition
reset_meta_service();
partition_pb.set_state(RecyclePartitionPB::DROPPED);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
EXPECT_TRUE(is_dropped_tablet(txn.get(), instance_id, index_id, partition_id));
reset_meta_service();
partition_pb.set_state(RecyclePartitionPB::RECYCLING);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
EXPECT_TRUE(is_dropped_tablet(txn.get(), instance_id, index_id, partition_id));
}
TEST(MetaServiceTest, IndexRequest) {
auto meta_service = get_meta_service();
std::string instance_id = "IndexRequest";
auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->enable_processing();
auto reset_meta_service = [&meta_service] { meta_service = get_meta_service(); };
constexpr int64_t table_id = 10001;
constexpr int64_t index_id = 10002;
constexpr int64_t partition_id = 10003;
constexpr int64_t tablet_id = 10004;
std::unique_ptr<Transaction> txn;
doris::TabletMetaCloudPB tablet_pb;
tablet_pb.set_table_id(table_id);
tablet_pb.set_index_id(index_id);
tablet_pb.set_partition_id(partition_id);
tablet_pb.set_tablet_id(tablet_id);
auto tablet_key = meta_tablet_key({instance_id, table_id, index_id, partition_id, tablet_id});
auto tablet_val = tablet_pb.SerializeAsString();
RecycleIndexPB index_pb;
auto index_key = recycle_index_key({instance_id, index_id});
int64_t val_int = 0;
auto tbl_version_key = table_version_key({instance_id, 1, table_id});
std::string val;
// ------------Test prepare index------------
brpc::Controller ctrl;
IndexRequest req;
IndexResponse res;
meta_service->prepare_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
req.set_db_id(1);
req.set_table_id(table_id);
req.add_index_ids(index_id);
req.set_is_new_table(true);
// Last state UNKNOWN
res.Clear();
meta_service->prepare_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(index_pb.ParseFromString(val));
ASSERT_EQ(index_pb.state(), RecycleIndexPB::PREPARED);
// Last state PREPARED
res.Clear();
meta_service->prepare_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(index_pb.ParseFromString(val));
ASSERT_EQ(index_pb.state(), RecycleIndexPB::PREPARED);
// Last state DROPPED
reset_meta_service();
index_pb.set_state(RecycleIndexPB::DROPPED);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->prepare_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(index_pb.ParseFromString(val));
ASSERT_EQ(index_pb.state(), RecycleIndexPB::DROPPED);
// Last state RECYCLING
reset_meta_service();
index_pb.set_state(RecycleIndexPB::RECYCLING);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->prepare_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(index_pb.ParseFromString(val));
ASSERT_EQ(index_pb.state(), RecycleIndexPB::RECYCLING);
// Last state UNKNOWN but tablet meta existed
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->remove(index_key);
txn->put(tablet_key, tablet_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->prepare_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::ALREADY_EXISTED);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// Prepare index should not init table version
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// ------------Test commit index------------
reset_meta_service();
req.Clear();
meta_service->commit_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
req.set_db_id(1);
req.set_table_id(table_id);
req.add_index_ids(index_id);
req.set_is_new_table(true);
// Last state UNKNOWN
res.Clear();
meta_service->commit_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// Last state PREPARED
reset_meta_service();
index_pb.set_state(RecycleIndexPB::PREPARED);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->commit_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// First commit index should init table version
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 1);
// Last state DROPPED
reset_meta_service();
index_pb.set_state(RecycleIndexPB::DROPPED);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->commit_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(index_pb.ParseFromString(val));
ASSERT_EQ(index_pb.state(), RecycleIndexPB::DROPPED);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// Last state RECYCLING
reset_meta_service();
index_pb.set_state(RecycleIndexPB::RECYCLING);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->commit_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(index_pb.ParseFromString(val));
ASSERT_EQ(index_pb.state(), RecycleIndexPB::RECYCLING);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// Last state UNKNOWN but tablet meta existed
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(tablet_key, tablet_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->commit_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// ------------Test drop index------------
reset_meta_service();
req.Clear();
meta_service->drop_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
req.set_db_id(1);
req.set_table_id(table_id);
req.add_index_ids(index_id);
req.set_is_new_table(true);
// Last state UNKNOWN
res.Clear();
meta_service->drop_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(index_pb.ParseFromString(val));
ASSERT_EQ(index_pb.state(), RecycleIndexPB::DROPPED);
// Last state PREPARED
reset_meta_service();
index_pb.set_state(RecycleIndexPB::PREPARED);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->drop_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(index_pb.ParseFromString(val));
ASSERT_EQ(index_pb.state(), RecycleIndexPB::DROPPED);
// Last state DROPPED
reset_meta_service();
index_pb.set_state(RecycleIndexPB::DROPPED);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->drop_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(index_pb.ParseFromString(val));
ASSERT_EQ(index_pb.state(), RecycleIndexPB::DROPPED);
// Last state RECYCLING
reset_meta_service();
index_pb.set_state(RecycleIndexPB::RECYCLING);
val = index_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(index_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->drop_index(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(index_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(index_pb.ParseFromString(val));
ASSERT_EQ(index_pb.state(), RecycleIndexPB::RECYCLING);
// Drop index should not init table version
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
}
TEST(MetaServiceTest, PartitionRequest) {
auto meta_service = get_meta_service();
std::string instance_id = "PartitionRequest";
auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->enable_processing();
auto reset_meta_service = [&meta_service] { meta_service = get_meta_service(); };
constexpr int64_t table_id = 10001;
constexpr int64_t index_id = 10002;
constexpr int64_t partition_id = 10003;
constexpr int64_t tablet_id = 10004;
std::unique_ptr<Transaction> txn;
doris::TabletMetaCloudPB tablet_pb;
tablet_pb.set_table_id(table_id);
tablet_pb.set_index_id(index_id);
tablet_pb.set_partition_id(partition_id);
tablet_pb.set_tablet_id(tablet_id);
auto tablet_key = meta_tablet_key({instance_id, table_id, index_id, partition_id, tablet_id});
auto tablet_val = tablet_pb.SerializeAsString();
RecyclePartitionPB partition_pb;
auto partition_key = recycle_partition_key({instance_id, partition_id});
int64_t val_int = 0;
auto tbl_version_key = table_version_key({instance_id, 1, table_id});
std::string val;
// ------------Test prepare partition------------
brpc::Controller ctrl;
PartitionRequest req;
PartitionResponse res;
meta_service->prepare_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
req.set_db_id(1);
req.set_table_id(table_id);
req.add_index_ids(index_id);
req.add_partition_ids(partition_id);
// Last state UNKNOWN
res.Clear();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->atomic_add(tbl_version_key, 1);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
meta_service->prepare_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::PREPARED);
// Prepare partition should not update table version
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 1);
// Last state PREPARED
res.Clear();
meta_service->prepare_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::PREPARED);
// Last state DROPPED
reset_meta_service();
partition_pb.set_state(RecyclePartitionPB::DROPPED);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->prepare_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::DROPPED);
// Last state RECYCLING
reset_meta_service();
partition_pb.set_state(RecyclePartitionPB::RECYCLING);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->prepare_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::RECYCLING);
// Last state UNKNOWN but tablet meta existed
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->remove(partition_key);
txn->put(tablet_key, tablet_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->prepare_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::ALREADY_EXISTED);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// ------------Test commit partition------------
reset_meta_service();
req.Clear();
meta_service->commit_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
req.set_db_id(1);
req.set_table_id(table_id);
req.add_index_ids(index_id);
req.add_partition_ids(partition_id);
// Last state UNKNOWN
res.Clear();
meta_service->commit_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// Last state PREPARED
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->atomic_add(tbl_version_key, 1);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
partition_pb.set_state(RecyclePartitionPB::PREPARED);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->commit_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
// Commit partition should update table version
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 2);
// Last state DROPPED
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->atomic_add(tbl_version_key, 1);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
partition_pb.set_state(RecyclePartitionPB::DROPPED);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->commit_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::DROPPED);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 1);
// Last state RECYCLING
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->atomic_add(tbl_version_key, 1);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
partition_pb.set_state(RecyclePartitionPB::RECYCLING);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->commit_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::RECYCLING);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 1);
// Last state UNKNOWN but tablet meta existed
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->atomic_add(tbl_version_key, 1);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(tablet_key, tablet_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->commit_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 1);
// Last state UNKNOWN and tablet meta existed, but request has no index ids
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(tablet_key, tablet_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
req.clear_index_ids();
meta_service->commit_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
req.add_index_ids(index_id);
// ------------Test check partition-----------
// Normal
req.set_db_id(1);
req.set_table_id(table_id + 1);
req.add_index_ids(index_id + 1);
req.add_partition_ids(partition_id + 1);
meta_service->prepare_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
meta_service->commit_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
CheckKVRequest req_check;
CheckKVResponse res_check;
meta_service->check_kv(&ctrl, &req_check, &res_check, nullptr);
ASSERT_EQ(res_check.status().code(), MetaServiceCode::INVALID_ARGUMENT);
res_check.Clear();
req_check.set_op(CheckKVRequest::CREATE_PARTITION_AFTER_FE_COMMIT);
CheckKeyInfos check_keys_pb;
check_keys_pb.add_table_ids(table_id + 1);
check_keys_pb.add_index_ids(index_id + 1);
check_keys_pb.add_partition_ids(partition_id + 1);
req_check.mutable_check_keys()->CopyFrom(check_keys_pb);
meta_service->check_kv(&ctrl, &req_check, &res_check, nullptr);
ASSERT_EQ(res_check.status().code(), MetaServiceCode::OK);
res_check.Clear();
// AbNomal not commit
req.Clear();
req.set_db_id(1);
req.set_table_id(table_id + 2);
req.add_index_ids(index_id + 2);
req.add_partition_ids(partition_id + 2);
meta_service->prepare_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
req_check.Clear();
req_check.set_op(CheckKVRequest::CREATE_PARTITION_AFTER_FE_COMMIT);
check_keys_pb.Clear();
check_keys_pb.add_table_ids(table_id + 2);
check_keys_pb.add_index_ids(index_id + 2);
check_keys_pb.add_partition_ids(partition_id + 2);
req_check.mutable_check_keys()->CopyFrom(check_keys_pb);
meta_service->check_kv(&ctrl, &req_check, &res_check, nullptr);
ASSERT_EQ(res_check.status().code(), MetaServiceCode::UNDEFINED_ERR);
// ------------Test check index-----------
// Normal
IndexRequest req_index;
IndexResponse res_index;
req_index.set_db_id(1);
req_index.set_table_id(table_id + 3);
req_index.add_index_ids(index_id + 3);
meta_service->prepare_index(&ctrl, &req_index, &res_index, nullptr);
ASSERT_EQ(res_index.status().code(), MetaServiceCode::OK);
meta_service->commit_index(&ctrl, &req_index, &res_index, nullptr);
ASSERT_EQ(res_index.status().code(), MetaServiceCode::OK);
req_check.Clear();
res_check.Clear();
req_check.set_op(CheckKVRequest::CREATE_INDEX_AFTER_FE_COMMIT);
check_keys_pb.Clear();
check_keys_pb.add_table_ids(table_id + 3);
check_keys_pb.add_index_ids(index_id + 3);
req_check.mutable_check_keys()->CopyFrom(check_keys_pb);
meta_service->check_kv(&ctrl, &req_check, &res_check, nullptr);
ASSERT_EQ(res_check.status().code(), MetaServiceCode::OK);
res_check.Clear();
// ------------Test drop partition------------
reset_meta_service();
req.Clear();
meta_service->drop_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
req.set_db_id(1);
req.set_table_id(table_id);
req.add_index_ids(index_id);
req.add_partition_ids(partition_id);
req.set_need_update_table_version(true);
// Last state UNKNOWN
res.Clear();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->atomic_add(tbl_version_key, 1);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
meta_service->drop_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::DROPPED);
// Drop partition should update table version
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 2);
// Last state PREPARED
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->atomic_add(tbl_version_key, 1);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
partition_pb.set_state(RecyclePartitionPB::PREPARED);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->drop_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::DROPPED);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 2);
// Last state PREPARED but drop an empty partition
req.set_need_update_table_version(false);
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->atomic_add(tbl_version_key, 1);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
partition_pb.set_state(RecyclePartitionPB::PREPARED);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->drop_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::DROPPED);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 1);
// Last state DROPPED
reset_meta_service();
req.set_need_update_table_version(true);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->atomic_add(tbl_version_key, 1);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
partition_pb.set_state(RecyclePartitionPB::DROPPED);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->drop_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::DROPPED);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 1);
// Last state RECYCLING
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->atomic_add(tbl_version_key, 1);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
partition_pb.set_state(RecyclePartitionPB::RECYCLING);
val = partition_pb.SerializeAsString();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(partition_key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
res.Clear();
meta_service->drop_partition(&ctrl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(partition_key, &val), TxnErrorCode::TXN_OK);
ASSERT_TRUE(partition_pb.ParseFromString(val));
ASSERT_EQ(partition_pb.state(), RecyclePartitionPB::RECYCLING);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tbl_version_key, &val), TxnErrorCode::TXN_OK);
val_int = *reinterpret_cast<const int64_t*>(val.data());
ASSERT_EQ(val_int, 1);
}
TEST(MetaServiceTxnStoreRetryableTest, MockGetVersion) {
size_t index = 0;
SyncPoint::get_instance()->set_call_back("get_version_code", [&](auto&& args) {
LOG(INFO) << "GET_VERSION_CODE";
if (++index < 2) {
*doris::try_any_cast<MetaServiceCode*>(args[0]) =
MetaServiceCode::KV_TXN_STORE_GET_RETRYABLE;
}
});
SyncPoint::get_instance()->enable_processing();
auto service = get_meta_service();
create_tablet(service.get(), 1, 1, 1, 1);
insert_rowset(service.get(), 1, std::to_string(1), 1, 1, 1);
brpc::Controller ctrl;
GetVersionRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(1);
req.set_table_id(1);
req.set_partition_id(1);
GetVersionResponse resp;
service->get_version(&ctrl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::OK)
<< " status is " << resp.status().msg() << ", code=" << resp.status().code();
EXPECT_EQ(resp.version(), 2);
EXPECT_GE(index, 2);
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTxnStoreRetryableTest, DoNotReturnRetryableCode) {
SyncPoint::get_instance()->set_call_back("get_version_code", [&](auto&& args) {
*doris::try_any_cast<MetaServiceCode*>(args[0]) =
MetaServiceCode::KV_TXN_STORE_GET_RETRYABLE;
});
SyncPoint::get_instance()->enable_processing();
int32_t retry_times = config::txn_store_retry_times;
config::txn_store_retry_times = 3;
auto service = get_meta_service();
create_tablet(service.get(), 1, 1, 1, 1);
insert_rowset(service.get(), 1, std::to_string(1), 1, 1, 1);
brpc::Controller ctrl;
GetVersionRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(1);
req.set_table_id(1);
req.set_partition_id(1);
GetVersionResponse resp;
service->get_version(&ctrl, &req, &resp, nullptr);
ASSERT_EQ(resp.status().code(), MetaServiceCode::KV_TXN_GET_ERR)
<< " status is " << resp.status().msg() << ", code=" << resp.status().code();
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
config::txn_store_retry_times = retry_times;
}
TEST(MetaServiceTest, GetClusterStatusTest) {
auto meta_service = get_meta_service();
// add cluster first
InstanceKeyInfo key_info {mock_instance};
std::string key;
std::string val;
instance_key(key_info, &key);
InstanceInfoPB instance;
instance.set_instance_id(mock_instance);
ClusterPB c1;
c1.set_type(ClusterPB::COMPUTE);
c1.set_cluster_name(mock_cluster_name);
c1.set_cluster_id(mock_cluster_id);
c1.add_mysql_user_name()->append("m1");
c1.set_cluster_status(ClusterStatus::NORMAL);
ClusterPB c2;
c2.set_type(ClusterPB::COMPUTE);
c2.set_cluster_name(mock_cluster_name + "2");
c2.set_cluster_id(mock_cluster_id + "2");
c2.add_mysql_user_name()->append("m2");
c2.set_cluster_status(ClusterStatus::SUSPENDED);
ClusterPB c3;
c3.set_type(ClusterPB::COMPUTE);
c3.set_cluster_name(mock_cluster_name + "3");
c3.set_cluster_id(mock_cluster_id + "3");
c3.add_mysql_user_name()->append("m3");
c3.set_cluster_status(ClusterStatus::TO_RESUME);
instance.add_clusters()->CopyFrom(c1);
instance.add_clusters()->CopyFrom(c2);
instance.add_clusters()->CopyFrom(c3);
val = instance.SerializeAsString();
std::unique_ptr<Transaction> txn;
std::string get_val;
TxnErrorCode err = meta_service->txn_kv()->create_txn(&txn);
ASSERT_EQ(err, TxnErrorCode::TXN_OK);
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
// case: get all cluster
{
brpc::Controller cntl;
GetClusterStatusRequest req;
req.add_instance_ids(mock_instance);
GetClusterStatusResponse res;
meta_service->get_cluster_status(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.details().at(0).clusters().size(), 3);
}
// get normal cluster
{
brpc::Controller cntl;
GetClusterStatusRequest req;
req.add_instance_ids(mock_instance);
req.set_status(NORMAL);
GetClusterStatusResponse res;
meta_service->get_cluster_status(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
ASSERT_EQ(res.details().at(0).clusters().size(), 1);
}
}
TEST(MetaServiceTest, DecryptInfoTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("decrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* key = try_any_cast<std::string*>(args[0]);
*key = "selectdbselectdbselectdbselectdb";
auto* ret = try_any_cast<int*>(args[1]);
*ret = 0;
});
InstanceInfoPB instance;
EncryptionInfoPB encryption_info;
encryption_info.set_encryption_method("AES_256_ECB");
encryption_info.set_key_id(1);
std::string cipher_sk = "JUkuTDctR+ckJtnPkLScWaQZRcOtWBhsLLpnCRxQLxr734qB8cs6gNLH6grE1FxO";
std::string plain_sk = "Hx60p12123af234541nsVsffdfsdfghsdfhsdf34t";
ObjectStoreInfoPB obj_info;
obj_info.mutable_encryption_info()->CopyFrom(encryption_info);
obj_info.set_ak("akak1");
obj_info.set_sk(cipher_sk);
instance.add_obj_info()->CopyFrom(obj_info);
RamUserPB ram_user;
ram_user.set_ak("akak2");
ram_user.set_sk(cipher_sk);
ram_user.mutable_encryption_info()->CopyFrom(encryption_info);
instance.mutable_ram_user()->CopyFrom(ram_user);
StagePB stage;
stage.mutable_obj_info()->CopyFrom(obj_info);
instance.add_stages()->CopyFrom(stage);
auto checkcheck = [&](const InstanceInfoPB& instance) {
ASSERT_EQ(instance.obj_info(0).ak(), "akak1");
ASSERT_EQ(instance.obj_info(0).sk(), plain_sk);
ASSERT_EQ(instance.ram_user().ak(), "akak2");
ASSERT_EQ(instance.ram_user().sk(), plain_sk);
ASSERT_EQ(instance.stages(0).obj_info().ak(), "akak1");
ASSERT_EQ(instance.stages(0).obj_info().sk(), plain_sk);
};
std::string instance_id = "i1";
MetaServiceCode code;
std::string msg;
// No system_meta_service_arn_info_key
{
std::unique_ptr<Transaction> txn0;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn0), TxnErrorCode::TXN_OK);
std::shared_ptr<Transaction> txn(txn0.release());
InstanceInfoPB decrypt_instance;
decrypt_instance.CopyFrom(instance);
int ret = decrypt_instance_info(decrypt_instance, instance_id, code, msg, txn);
ASSERT_EQ(ret, 0);
checkcheck(decrypt_instance);
ASSERT_EQ(decrypt_instance.iam_user().user_id(), config::arn_id);
ASSERT_EQ(decrypt_instance.iam_user().external_id(), instance_id);
ASSERT_EQ(decrypt_instance.iam_user().ak(), config::arn_ak);
ASSERT_EQ(decrypt_instance.iam_user().sk(), config::arn_sk);
}
// With system_meta_service_arn_info_key
{
std::string key = system_meta_service_arn_info_key();
std::string val;
std::unique_ptr<Transaction> txn2;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn2), TxnErrorCode::TXN_OK);
RamUserPB iam_user;
iam_user.set_user_id("1234");
iam_user.set_ak("aksk3");
iam_user.set_sk(cipher_sk);
iam_user.set_external_id(instance_id);
iam_user.mutable_encryption_info()->CopyFrom(encryption_info);
val = iam_user.SerializeAsString();
txn2->put(key, val);
ASSERT_EQ(txn2->commit(), TxnErrorCode::TXN_OK);
std::unique_ptr<Transaction> txn0;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn0), TxnErrorCode::TXN_OK);
std::shared_ptr<Transaction> txn(txn0.release());
InstanceInfoPB decrypt_instance;
decrypt_instance.CopyFrom(instance);
int ret = decrypt_instance_info(decrypt_instance, instance_id, code, msg, txn);
ASSERT_EQ(ret, 0);
checkcheck(decrypt_instance);
ASSERT_EQ(decrypt_instance.iam_user().user_id(), "1234");
ASSERT_EQ(decrypt_instance.iam_user().external_id(), instance_id);
ASSERT_EQ(decrypt_instance.iam_user().ak(), "aksk3");
ASSERT_EQ(decrypt_instance.iam_user().sk(), plain_sk);
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, LegacyUpdateAkSkTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ObjectStoreInfoPB obj_info;
obj_info.set_id("1");
obj_info.set_ak("ak");
obj_info.set_sk("sk");
InstanceInfoPB instance;
instance.add_obj_info()->CopyFrom(obj_info);
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
std::string cipher_sk = "JUkuTDctR+ckJtnPkLScWaQZRcOtWBhsLLpnCRxQLxr734qB8cs6gNLH6grE1FxO";
std::string plain_sk = "Hx60p12123af234541nsVsffdfsdfghsdfhsdf34t";
// update failed
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::LEGACY_UPDATE_AK_SK);
req.mutable_obj()->set_id("2");
req.mutable_obj()->set_ak("new_ak");
req.mutable_obj()->set_sk(plain_sk);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(instance.obj_info(0).id(), "1");
ASSERT_EQ(instance.obj_info(0).ak(), "ak");
ASSERT_EQ(instance.obj_info(0).sk(), "sk");
}
// update successful
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::LEGACY_UPDATE_AK_SK);
req.mutable_obj()->set_id("1");
req.mutable_obj()->set_ak("new_ak");
req.mutable_obj()->set_sk(plain_sk);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(instance.obj_info(0).id(), "1");
ASSERT_EQ(instance.obj_info(0).ak(), "new_ak");
ASSERT_EQ(instance.obj_info(0).sk(), cipher_sk);
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
namespace detail {
bool normalize_hdfs_prefix(std::string& prefix);
bool normalize_hdfs_fs_name(std::string& fs_name);
} // namespace detail
TEST(MetaServiceTest, NormalizeHdfsConfTest) {
using namespace detail;
std::string prefix = "hdfs://127.0.0.1:8020/test";
EXPECT_FALSE(normalize_hdfs_prefix(prefix));
prefix = "test";
EXPECT_TRUE(normalize_hdfs_prefix(prefix));
EXPECT_EQ(prefix, "test");
prefix = " test ";
EXPECT_TRUE(normalize_hdfs_prefix(prefix));
EXPECT_EQ(prefix, "test");
prefix = " /test// ";
EXPECT_TRUE(normalize_hdfs_prefix(prefix));
EXPECT_EQ(prefix, "test");
prefix = "/";
EXPECT_TRUE(normalize_hdfs_prefix(prefix));
EXPECT_EQ(prefix, "");
std::string fs_name;
EXPECT_FALSE(normalize_hdfs_fs_name(prefix));
fs_name = " hdfs://127.0.0.1:8020/ ";
EXPECT_TRUE(normalize_hdfs_fs_name(fs_name));
EXPECT_EQ(fs_name, "hdfs://127.0.0.1:8020");
}
TEST(MetaServiceTest, AddObjInfoTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
InstanceInfoPB instance;
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
// update failed
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_OBJ_INFO);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT) << res.status().msg();
}
// update successful
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_OBJ_INFO);
auto sp = SyncPoint::get_instance();
sp->set_call_back("create_object_info_with_encrypt", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
});
sp->enable_processing();
ObjectStoreInfoPB obj_info;
obj_info.set_ak("ak");
obj_info.set_sk("sk");
obj_info.set_bucket("bucket");
obj_info.set_prefix("prefix");
obj_info.set_endpoint("endpoint");
obj_info.set_region("region");
obj_info.set_provider(ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_COS);
obj_info.set_external_endpoint("external");
req.mutable_obj()->MergeFrom(obj_info);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
const auto& obj = instance.obj_info().at(0);
ASSERT_EQ(obj.id(), "1");
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, AddHdfsInfoTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ObjectStoreInfoPB obj_info;
obj_info.set_id("1");
obj_info.set_ak("ak");
obj_info.set_sk("sk");
InstanceInfoPB instance;
instance.add_obj_info()->CopyFrom(obj_info);
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
// update failed
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT) << res.status().msg();
}
// update successful
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("test_alter_add_hdfs_info");
HdfsVaultInfo params;
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
// Invalid fs name
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT) << res.status().msg();
req.mutable_vault()->mutable_hdfs_info()->mutable_build_conf()->set_fs_name(
"hdfs://ip:port");
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(*(instance.resource_ids().begin()), "2");
ASSERT_EQ(*(instance.storage_vault_names().begin()), "test_alter_add_hdfs_info");
}
// update failed because duplicate name
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("test_alter_add_hdfs_info");
HdfsVaultInfo params;
params.mutable_build_conf()->set_fs_name("hdfs://ip:port");
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::ALREADY_EXISTED) << res.status().msg();
}
// to test if the vault id is expected
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("test_alter_add_hdfs_info_1");
HdfsVaultInfo params;
params.mutable_build_conf()->set_fs_name("hdfs://ip:port");
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(*(instance.resource_ids().begin() + 1), "3");
ASSERT_EQ(*(instance.storage_vault_names().begin() + 1), "test_alter_add_hdfs_info_1");
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, DropHdfsInfoTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ObjectStoreInfoPB obj_info;
obj_info.set_id("1");
obj_info.set_ak("ak");
obj_info.set_sk("sk");
StorageVaultPB vault;
vault.set_name("test_hdfs_vault");
vault.set_id("2");
InstanceInfoPB instance;
instance.add_obj_info()->CopyFrom(obj_info);
instance.add_storage_vault_names(vault.name());
instance.add_resource_ids(vault.id());
val = instance.SerializeAsString();
txn->put(key, val);
txn->put(storage_vault_key({instance.instance_id(), "2"}), vault.SerializeAsString());
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
txn = nullptr;
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
// update failed because has no storage vault set
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::DROP_HDFS_INFO);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT) << res.status().msg();
}
// update failed because vault name does not exist
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::DROP_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("test_hdfs_vault_not_found");
HdfsVaultInfo params;
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::STORAGE_VAULT_NOT_FOUND)
<< res.status().msg();
}
// update successfully
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::DROP_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("test_hdfs_vault");
HdfsVaultInfo params;
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(instance.resource_ids().size(), 0);
ASSERT_EQ(instance.storage_vault_names().size(), 0);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
// To test we can not read the storage vault anymore
std::string vault_key = storage_vault_key({instance.instance_id(), "2"});
std::string vault_value;
auto code = txn->get(vault_key, &vault_value);
ASSERT_TRUE(code != TxnErrorCode::TXN_OK);
}
{
// Try to add one new hdfs info and then check the vault id is expected
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("test_alter_add_hdfs_info");
HdfsVaultInfo params;
HdfsBuildConf conf;
conf.set_fs_name("hdfs://127.0.0.1:8020");
params.mutable_build_conf()->MergeFrom(conf);
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(*(instance.resource_ids().begin()), "2");
ASSERT_EQ(*(instance.storage_vault_names().begin()), "test_alter_add_hdfs_info");
}
// Add two more vaults
{
// Try to add one new hdfs info and then check the vault id is expected
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("test_alter_add_hdfs_info_1");
HdfsVaultInfo params;
HdfsBuildConf conf;
conf.set_fs_name("hdfs://127.0.0.1:8020");
params.mutable_build_conf()->MergeFrom(conf);
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(instance.resource_ids().at(1), "3");
ASSERT_EQ(instance.storage_vault_names().at(1), "test_alter_add_hdfs_info_1");
}
{
// Try to add one new hdfs info and then check the vault id is expected
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("test_alter_add_hdfs_info_2");
HdfsVaultInfo params;
HdfsBuildConf conf;
conf.set_fs_name("hdfs://127.0.0.1:8020");
params.mutable_build_conf()->MergeFrom(conf);
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(instance.resource_ids().at(2), "4");
ASSERT_EQ(instance.storage_vault_names().at(2), "test_alter_add_hdfs_info_2");
}
// Remove one vault among three vaults
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::DROP_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("test_alter_add_hdfs_info_1");
HdfsVaultInfo params;
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(instance.resource_ids().size(), 2);
ASSERT_EQ(instance.storage_vault_names().size(), 2);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
// To test we can not read the storage vault anymore
std::string vault_key = storage_vault_key({instance.instance_id(), "3"});
std::string vault_value;
auto code = txn->get(vault_key, &vault_value);
ASSERT_TRUE(code != TxnErrorCode::TXN_OK);
ASSERT_EQ(2, instance.resource_ids().size());
ASSERT_EQ(2, instance.storage_vault_names().size());
ASSERT_EQ(instance.resource_ids().at(0), "2");
ASSERT_EQ(instance.storage_vault_names().at(0), "test_alter_add_hdfs_info");
ASSERT_EQ(instance.resource_ids().at(1), "4");
ASSERT_EQ(instance.storage_vault_names().at(1), "test_alter_add_hdfs_info_2");
}
{
// Try to add one new hdfs info and then check the vault id is expected
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("test_alter_add_hdfs_info_3");
HdfsVaultInfo params;
HdfsBuildConf conf;
conf.set_fs_name("hdfs://127.0.0.1:8020");
params.mutable_build_conf()->MergeFrom(conf);
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(instance.resource_ids().at(2), "5");
ASSERT_EQ(instance.storage_vault_names().at(2), "test_alter_add_hdfs_info_3");
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, GetDefaultVaultTest) {
auto meta_service = get_meta_service();
auto get_test_instance = [&](InstanceInfoPB& i, std::string instance_id) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {std::move(instance_id)};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
// case: normal create instance with hdfs info
{
brpc::Controller cntl;
CreateInstanceRequest req;
std::string instance_id = "test_instance_with_hdfs_info";
req.set_instance_id(instance_id);
req.set_user_id("test_user");
req.set_name("test_name");
HdfsVaultInfo hdfs;
HdfsBuildConf conf;
conf.set_fs_name("hdfs://127.0.0.1:8020");
conf.set_user("test_user");
hdfs.mutable_build_conf()->CopyFrom(conf);
StorageVaultPB vault;
vault.mutable_hdfs_info()->CopyFrom(hdfs);
req.mutable_vault()->CopyFrom(vault);
auto sp = SyncPoint::get_instance();
sp->set_call_back("create_object_info_with_encrypt", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
});
sp->enable_processing();
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
InstanceInfoPB i;
get_test_instance(i, instance_id);
// It wouldn't be set
ASSERT_EQ(i.default_storage_vault_id(), "");
ASSERT_EQ(i.default_storage_vault_name(), "");
ASSERT_EQ(i.resource_ids().at(0), "1");
ASSERT_EQ(i.storage_vault_names().at(0), "built_in_storage_vault");
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
// case: normal create instance with obj info
{
brpc::Controller cntl;
CreateInstanceRequest req;
std::string instance_id = "test_instance_with_s3_info";
req.set_instance_id(instance_id);
req.set_user_id("test_user");
req.set_name("test_name");
InstanceInfoPB instance;
EncryptionInfoPB encryption_info;
encryption_info.set_encryption_method("AES_256_ECB");
encryption_info.set_key_id(1);
std::string cipher_sk = "JUkuTDctR+ckJtnPkLScWaQZRcOtWBhsLLpnCRxQLxr734qB8cs6gNLH6grE1FxO";
std::string plain_sk = "Hx60p12123af234541nsVsffdfsdfghsdfhsdf34t";
ObjectStoreInfoPB obj_info;
obj_info.mutable_encryption_info()->CopyFrom(encryption_info);
obj_info.set_ak("akak1");
obj_info.set_sk(cipher_sk);
obj_info.set_endpoint("selectdb");
obj_info.set_external_endpoint("velodb");
obj_info.set_bucket("gavin");
obj_info.set_region("American");
obj_info.set_provider(ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
instance.add_obj_info()->CopyFrom(obj_info);
req.mutable_obj_info()->CopyFrom(obj_info);
auto sp = SyncPoint::get_instance();
sp->set_call_back("create_object_info_with_encrypt", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* code = try_any_cast<MetaServiceCode*>(args[1]);
*code = MetaServiceCode::OK;
auto* msg = try_any_cast<std::string*>(args[2]);
*msg = "";
});
sp->enable_processing();
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
InstanceInfoPB i;
get_test_instance(i, instance_id);
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
}
TEST(MetaServiceTest, SetDefaultVaultTest) {
auto meta_service = get_meta_service();
std::string instance_id = "test_instance";
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {instance_id};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
brpc::Controller cntl;
CreateInstanceRequest req;
req.set_instance_id(instance_id);
req.set_user_id("test_user");
req.set_name("test_name");
HdfsVaultInfo hdfs;
HdfsBuildConf conf;
conf.set_fs_name("hdfs://127.0.0.1:8020");
conf.set_user("test_user");
hdfs.mutable_build_conf()->CopyFrom(conf);
StorageVaultPB vault;
vault.mutable_hdfs_info()->CopyFrom(hdfs);
req.mutable_vault()->CopyFrom(vault);
auto sp = SyncPoint::get_instance();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->enable_processing();
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
InstanceInfoPB i;
get_test_instance(i);
ASSERT_EQ(i.default_storage_vault_id(), "");
ASSERT_EQ(i.default_storage_vault_name(), "");
ASSERT_EQ(i.resource_ids().at(0), "1");
ASSERT_EQ(i.storage_vault_names().at(0), "built_in_storage_vault");
for (size_t i = 0; i < 20; i++) {
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
StorageVaultPB hdfs;
auto name = fmt::format("test_alter_add_hdfs_info_{}", i);
hdfs.set_name(name);
HdfsVaultInfo params;
HdfsBuildConf conf;
conf.set_fs_name("hdfs://127.0.0.1:8020");
params.mutable_build_conf()->MergeFrom(conf);
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
AlterObjStoreInfoRequest set_default_req;
set_default_req.set_cloud_unique_id("test_cloud_unique_id");
set_default_req.set_op(AlterObjStoreInfoRequest::SET_DEFAULT_VAULT);
set_default_req.mutable_vault()->CopyFrom(hdfs);
AlterObjStoreInfoResponse set_default_res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &set_default_req,
&set_default_res, nullptr);
ASSERT_EQ(set_default_res.status().code(), MetaServiceCode::OK)
<< set_default_res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(std::to_string(i + 2), instance.default_storage_vault_id());
}
// Try to set one non-existent vault as default
{
StorageVaultPB hdfs;
auto name = "test_alter_add_hdfs_info_no";
hdfs.set_name(name);
HdfsVaultInfo params;
hdfs.mutable_hdfs_info()->CopyFrom(params);
AlterObjStoreInfoRequest set_default_req;
set_default_req.set_cloud_unique_id("test_cloud_unique_id");
set_default_req.set_op(AlterObjStoreInfoRequest::SET_DEFAULT_VAULT);
set_default_req.mutable_vault()->CopyFrom(hdfs);
AlterObjStoreInfoResponse set_default_res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &set_default_req,
&set_default_res, nullptr);
ASSERT_NE(set_default_res.status().code(), MetaServiceCode::OK)
<< set_default_res.status().msg();
}
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
TEST(MetaServiceTest, GetObjStoreInfoTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ObjectStoreInfoPB obj_info;
obj_info.set_id("1");
obj_info.set_ak("ak");
obj_info.set_sk("sk");
StorageVaultPB vault;
vault.set_name("test_hdfs_vault");
vault.set_id("2");
InstanceInfoPB instance;
instance.add_obj_info()->CopyFrom(obj_info);
instance.add_storage_vault_names(vault.name());
instance.add_resource_ids(vault.id());
instance.set_instance_id("GetObjStoreInfoTestInstance");
val = instance.SerializeAsString();
txn->put(key, val);
txn->put(storage_vault_key({instance.instance_id(), "2"}), vault.SerializeAsString());
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
txn = nullptr;
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
for (size_t i = 0; i < 20; i++) {
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
StorageVaultPB hdfs;
auto name = fmt::format("test_alter_add_hdfs_info_{}", i);
hdfs.set_name(name);
HdfsVaultInfo params;
HdfsBuildConf conf;
conf.set_fs_name("hdfs://127.0.0.1:8020");
params.mutable_build_conf()->MergeFrom(conf);
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_TRUE(std::find_if(instance.resource_ids().begin(), instance.resource_ids().end(),
[&](const auto& id) { return id == std::to_string(i + 3); }) !=
instance.resource_ids().end());
ASSERT_TRUE(std::find_if(instance.storage_vault_names().begin(),
instance.storage_vault_names().end(), [&](const auto& vault_name) {
return name == vault_name;
}) != instance.storage_vault_names().end());
}
{
brpc::Controller cntl;
GetObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
GetObjStoreInfoResponse res;
meta_service->get_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
ASSERT_EQ(res.storage_vault().size(), 21);
const auto& vaults = res.storage_vault();
for (size_t i = 0; i < 20; i++) {
auto id = std::to_string(i + 3);
auto name = fmt::format("test_alter_add_hdfs_info_{}", i);
ASSERT_TRUE(std::find_if(vaults.begin(), vaults.end(), [&](const auto& vault) {
return id == vault.id();
}) != vaults.end());
ASSERT_TRUE(std::find_if(vaults.begin(), vaults.end(), [&](const auto& vault) {
return name == vault.name();
}) != vaults.end());
}
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, CreateVersionedTablet) {
auto meta_service = get_meta_service(false);
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::string instance_id = "test_instance";
{
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
InstanceInfoPB instance;
instance.set_multi_version_status(MultiVersionStatus::MULTI_VERSION_WRITE_ONLY);
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
meta_service->resource_mgr()->refresh_instance(instance_id);
}
int64_t db_id = 1, table_id = 2, index_id = 3, partition_id = 4, tablet_id = 5;
{
brpc::Controller cntl;
CreateTabletsRequest req;
CreateTabletsResponse res;
req.set_cloud_unique_id(fmt::format("1:{}:1", instance_id));
req.set_db_id(db_id);
add_tablet(req, table_id, index_id, partition_id, tablet_id);
meta_service->create_tablets(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << tablet_id;
}
Versionstamp commit_versionstamp;
{
// verify versioned tablet meta is written
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string val;
std::string key = versioned::meta_tablet_key({instance_id, tablet_id});
ASSERT_EQ(versioned_get(txn.get(), key, &commit_versionstamp, &val), TxnErrorCode::TXN_OK)
<< hex(key);
TabletMetaCloudPB versioned_tablet_meta;
versioned_tablet_meta.ParseFromString(val);
ASSERT_EQ(versioned_tablet_meta.table_id(), table_id);
ASSERT_EQ(versioned_tablet_meta.index_id(), index_id)
<< versioned_tablet_meta.ShortDebugString();
ASSERT_EQ(versioned_tablet_meta.partition_id(), partition_id);
ASSERT_EQ(versioned_tablet_meta.tablet_id(), tablet_id);
}
{
// verify versioned tablet index/inverted index is written
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key = versioned::tablet_index_key({instance_id, tablet_id});
std::string val;
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
TabletIndexPB tablet_index;
tablet_index.ParseFromString(val);
ASSERT_EQ(tablet_index.db_id(), db_id);
ASSERT_EQ(tablet_index.table_id(), table_id);
ASSERT_EQ(tablet_index.index_id(), index_id);
ASSERT_EQ(tablet_index.partition_id(), partition_id);
ASSERT_EQ(tablet_index.tablet_id(), tablet_id);
key = versioned::tablet_inverted_index_key(
{instance_id, db_id, table_id, index_id, partition_id, tablet_id});
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
}
{
// verify the first versioned rowset meta is written
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
int64_t end_version = 1; // from add_tablet
std::string key = versioned::meta_rowset_load_key({instance_id, tablet_id, end_version});
RowsetMetaCloudPB rowset_meta;
Versionstamp versionstamp;
ASSERT_EQ(versioned::document_get(txn.get(), key, &rowset_meta, &versionstamp),
TxnErrorCode::TXN_OK);
ASSERT_EQ(versionstamp, commit_versionstamp);
}
{
// verify the tablet load stats is written
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key = versioned::tablet_load_stats_key({instance_id, tablet_id});
TabletStatsPB load_stats;
Versionstamp versionstamp;
ASSERT_EQ(versioned::document_get(txn.get(), key, &load_stats, &versionstamp),
TxnErrorCode::TXN_OK);
ASSERT_EQ(versionstamp, commit_versionstamp);
}
{
// verify the tablet compact stats is written
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key = versioned::tablet_compact_stats_key({instance_id, tablet_id});
TabletStatsPB compact_stats;
Versionstamp versionstamp;
ASSERT_EQ(versioned::document_get(txn.get(), key, &compact_stats, &versionstamp),
TxnErrorCode::TXN_OK);
ASSERT_EQ(versionstamp, commit_versionstamp);
EXPECT_EQ(compact_stats.cumulative_point(), 2);
}
{
// verify the tablet schema is written
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key = versioned::meta_schema_key({instance_id, index_id, 0});
doris::TabletSchemaCloudPB schema;
ASSERT_EQ(document_get(txn.get(), key, &schema), TxnErrorCode::TXN_OK);
EXPECT_EQ(schema.schema_version(), 0);
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, CreateTabletsVaultsTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
InstanceInfoPB instance;
instance.set_enable_storage_vault(true);
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
// tablet_metas_size is 0
{
CreateTabletsRequest request;
request.set_cloud_unique_id("test_cloud_unique_id");
brpc::Controller cntl;
CreateTabletsResponse response;
meta_service->create_tablets(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&request, &response, nullptr);
ASSERT_EQ(response.status().code(), MetaServiceCode::INVALID_ARGUMENT)
<< response.status().msg();
}
// try to use default
{
CreateTabletsRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_storage_vault_name("");
req.add_tablet_metas();
brpc::Controller cntl;
CreateTabletsResponse res;
meta_service->create_tablets(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
// failed because no default
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT) << res.status().msg();
}
// Create One Hdfs info as built_in vault
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_HDFS_INFO);
StorageVaultPB hdfs;
hdfs.set_name("built_in_storage_vault");
HdfsVaultInfo params;
params.mutable_build_conf()->set_fs_name("hdfs://ip:port");
hdfs.mutable_hdfs_info()->CopyFrom(params);
req.mutable_vault()->CopyFrom(hdfs);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB i;
get_test_instance(i);
ASSERT_EQ(i.default_storage_vault_id(), "");
ASSERT_EQ(i.default_storage_vault_name(), "");
ASSERT_EQ(i.resource_ids().at(0), "1");
ASSERT_EQ(i.storage_vault_names().at(0), "built_in_storage_vault");
}
// Try to set built_in_storage_vault vault as default
{
StorageVaultPB hdfs;
std::string name = "built_in_storage_vault";
hdfs.set_name(std::move(name));
HdfsVaultInfo params;
hdfs.mutable_hdfs_info()->CopyFrom(params);
AlterObjStoreInfoRequest set_default_req;
set_default_req.set_cloud_unique_id("test_cloud_unique_id");
set_default_req.set_op(AlterObjStoreInfoRequest::SET_DEFAULT_VAULT);
set_default_req.mutable_vault()->CopyFrom(hdfs);
AlterObjStoreInfoResponse set_default_res;
brpc::Controller cntl;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &set_default_req,
&set_default_res, nullptr);
ASSERT_EQ(set_default_res.status().code(), MetaServiceCode::OK)
<< set_default_res.status().msg();
}
// try to use default vault
{
CreateTabletsRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_storage_vault_name("");
req.add_tablet_metas();
auto sp = SyncPoint::get_instance();
sp->set_call_back("create_tablets",
[](auto&& args) { *try_any_cast<bool*>(args.back()) = true; });
sp->enable_processing();
brpc::Controller cntl;
CreateTabletsResponse res;
meta_service->create_tablets(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
ASSERT_EQ(res.storage_vault_id(), "1");
ASSERT_EQ(res.storage_vault_name(), "built_in_storage_vault");
sp->clear_call_back("create_tablets");
}
// try to use one non-existent vault
{
CreateTabletsRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_storage_vault_name("non-existent");
req.add_tablet_metas();
auto sp = SyncPoint::get_instance();
SyncPoint::CallbackGuard guard;
sp->set_call_back(
"create_tablets", [](auto&& args) { *try_any_cast<bool*>(args.back()) = true; },
&guard);
sp->enable_processing();
brpc::Controller cntl;
CreateTabletsResponse res;
meta_service->create_tablets(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT) << res.status().msg();
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, UpdateAkSkTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
std::string cipher_sk = "JUkuTDctR+ckJtnPkLScWaQZRcOtWBhsLLpnCRxQLxr734qB8cs6gNLH6grE1FxO";
std::string plain_sk = "Hx60p12123af234541nsVsffdfsdfghsdfhsdf34t";
auto update = [&](bool with_user_id, bool with_wrong_user_id) {
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ObjectStoreInfoPB obj_info;
if (with_user_id) {
obj_info.set_user_id("111");
}
obj_info.set_ak("ak");
obj_info.set_sk("sk");
InstanceInfoPB instance;
instance.add_obj_info()->CopyFrom(obj_info);
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
UpdateAkSkRequest req;
req.set_instance_id("test_instance");
RamUserPB ram_user;
if (with_wrong_user_id) {
ram_user.set_user_id("222");
} else {
ram_user.set_user_id("111");
}
ram_user.set_ak("new_ak");
ram_user.set_sk(plain_sk);
req.add_internal_bucket_user()->CopyFrom(ram_user);
brpc::Controller cntl;
UpdateAkSkResponse res;
meta_service->update_ak_sk(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
if (with_wrong_user_id) {
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
} else {
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
InstanceInfoPB update_instance;
get_test_instance(update_instance);
ASSERT_EQ(update_instance.obj_info(0).user_id(), "111");
ASSERT_EQ(update_instance.obj_info(0).ak(), "new_ak");
ASSERT_EQ(update_instance.obj_info(0).sk(), cipher_sk);
}
};
update(false, false);
update(true, false);
update(true, true);
}
TEST(MetaServiceTest, AlterIamTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::string cipher_sk = "JUkuTDctR+ckJtnPkLScWaQZRcOtWBhsLLpnCRxQLxr734qB8cs6gNLH6grE1FxO";
std::string plain_sk = "Hx60p12123af234541nsVsffdfsdfghsdfhsdf34t";
auto get_arn_info_key = [&](RamUserPB& i) {
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(system_meta_service_arn_info_key(), &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
// add new system_meta_service_arn_info_key
{
AlterIamRequest req;
req.set_account_id("123");
req.set_ak("ak1");
req.set_sk(plain_sk);
brpc::Controller cntl;
AlterIamResponse res;
meta_service->alter_iam(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
RamUserPB ram_user;
get_arn_info_key(ram_user);
ASSERT_EQ(ram_user.user_id(), "123");
ASSERT_EQ(ram_user.ak(), "ak1");
ASSERT_EQ(ram_user.sk(), cipher_sk);
}
// with old system_meta_service_arn_info_key
{
AlterIamRequest req;
req.set_account_id("321");
req.set_ak("ak2");
req.set_sk(plain_sk);
brpc::Controller cntl;
AlterIamResponse res;
meta_service->alter_iam(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
RamUserPB ram_user;
get_arn_info_key(ram_user);
ASSERT_EQ(ram_user.user_id(), "321");
ASSERT_EQ(ram_user.ak(), "ak2");
ASSERT_EQ(ram_user.sk(), cipher_sk);
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, UpdateTmpRowsetTest) {
auto meta_service = get_meta_service();
std::string instance_id = "update_rowset_meta_test_instance_id";
auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->enable_processing();
{
// 1. normal path
constexpr auto db_id = 10000, table_id = 10001, index_id = 10002, partition_id = 10003,
tablet_id = 10004;
int64_t txn_id = 0;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
ASSERT_NO_FATAL_FAILURE(begin_txn(meta_service.get(), db_id, label, table_id, txn_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
ASSERT_NO_FATAL_FAILURE(prepare_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
res.Clear();
rowset.set_rowset_state(doris::BEGIN_PARTIAL_UPDATE);
ASSERT_NO_FATAL_FAILURE(commit_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
res.Clear();
// simulate that there are new segments added to this rowset
rowset.set_num_segments(rowset.num_segments() + 3);
rowset.set_num_rows(rowset.num_rows() + 1000);
rowset.set_total_disk_size(rowset.total_disk_size() + 11000);
rowset.set_index_disk_size(rowset.index_disk_size() + 1000);
rowset.set_data_disk_size(rowset.data_disk_size() + 10000);
ASSERT_NO_FATAL_FAILURE(update_tmp_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
std::string key;
std::string val;
MetaRowsetTmpKeyInfo key_info {instance_id, txn_id, tablet_id};
meta_rowset_tmp_key(key_info, &key);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
doris::RowsetMetaCloudPB fetchedRowsetMeta;
ASSERT_TRUE(fetchedRowsetMeta.ParseFromString(val));
ASSERT_EQ(doris::BEGIN_PARTIAL_UPDATE, fetchedRowsetMeta.rowset_state());
ASSERT_EQ(rowset.num_segments(), fetchedRowsetMeta.num_segments());
ASSERT_EQ(rowset.num_rows(), fetchedRowsetMeta.num_rows());
ASSERT_EQ(rowset.total_disk_size(), fetchedRowsetMeta.total_disk_size());
ASSERT_EQ(rowset.index_disk_size(), fetchedRowsetMeta.index_disk_size());
ASSERT_EQ(rowset.data_disk_size(), fetchedRowsetMeta.data_disk_size());
ASSERT_NO_FATAL_FAILURE(commit_txn(meta_service.get(), db_id, txn_id, label));
}
{
// 2. rpc retryies due to network error will success
constexpr auto db_id = 20000, table_id = 20001, index_id = 20002, partition_id = 20003,
tablet_id = 20004;
int64_t txn_id = 0;
std::string label = "update_rowset_meta_test_label2";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
ASSERT_NO_FATAL_FAILURE(begin_txn(meta_service.get(), db_id, label, table_id, txn_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
ASSERT_NO_FATAL_FAILURE(prepare_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
res.Clear();
rowset.set_rowset_state(doris::BEGIN_PARTIAL_UPDATE);
ASSERT_NO_FATAL_FAILURE(commit_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
res.Clear();
// simulate that there are new segments added to this rowset
rowset.set_num_segments(rowset.num_segments() + 3);
rowset.set_num_rows(rowset.num_rows() + 1000);
rowset.set_total_disk_size(rowset.total_disk_size() + 11000);
rowset.set_index_disk_size(rowset.index_disk_size() + 1000);
rowset.set_data_disk_size(rowset.data_disk_size() + 10000);
// repeated calls to update_tmp_rowset will all success
ASSERT_NO_FATAL_FAILURE(update_tmp_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
ASSERT_NO_FATAL_FAILURE(update_tmp_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
ASSERT_NO_FATAL_FAILURE(update_tmp_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
ASSERT_NO_FATAL_FAILURE(update_tmp_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
std::string key;
std::string val;
MetaRowsetTmpKeyInfo key_info {instance_id, txn_id, tablet_id};
meta_rowset_tmp_key(key_info, &key);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
doris::RowsetMetaCloudPB fetchedRowsetMeta;
ASSERT_TRUE(fetchedRowsetMeta.ParseFromString(val));
ASSERT_EQ(doris::BEGIN_PARTIAL_UPDATE, fetchedRowsetMeta.rowset_state());
ASSERT_EQ(rowset.num_segments(), fetchedRowsetMeta.num_segments());
ASSERT_EQ(rowset.num_rows(), fetchedRowsetMeta.num_rows());
ASSERT_EQ(rowset.total_disk_size(), fetchedRowsetMeta.total_disk_size());
ASSERT_EQ(rowset.index_disk_size(), fetchedRowsetMeta.index_disk_size());
ASSERT_EQ(rowset.data_disk_size(), fetchedRowsetMeta.data_disk_size());
ASSERT_NO_FATAL_FAILURE(commit_txn(meta_service.get(), db_id, txn_id, label));
}
{
// 3. call update_tmp_rowset without commit_rowset first will fail
constexpr auto db_id = 30000, table_id = 30001, index_id = 30002, partition_id = 30003,
tablet_id = 30004;
int64_t txn_id = 0;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
ASSERT_NO_FATAL_FAILURE(begin_txn(meta_service.get(), db_id, label, table_id, txn_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
ASSERT_NO_FATAL_FAILURE(prepare_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
res.Clear();
// simulate that there are new segments added to this rowset
rowset.set_num_segments(rowset.num_segments() + 3);
rowset.set_num_rows(rowset.num_rows() + 1000);
rowset.set_total_disk_size(rowset.total_disk_size() + 11000);
rowset.set_index_disk_size(rowset.index_disk_size() + 1000);
rowset.set_data_disk_size(rowset.data_disk_size() + 10000);
ASSERT_NO_FATAL_FAILURE(update_tmp_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::ROWSET_META_NOT_FOUND) << label;
}
}
TEST(MetaServiceTest, CreateS3VaultWithIamRole) {
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::pair<std::string, std::string> pair;
sp->set_call_back("extract_object_storage_info:get_aksk_pair", [&](auto&& args) {
auto* ret = try_any_cast<std::pair<std::string, std::string>*>(args[0]);
pair = *ret;
});
auto meta_service = get_meta_service();
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ObjectStoreInfoPB obj_info;
obj_info.set_id("1");
obj_info.set_ak("ak");
obj_info.set_sk("sk");
StorageVaultPB vault;
constexpr char vault_name[] = "test_alter_s3_vault";
vault.mutable_obj_info()->MergeFrom(obj_info);
vault.set_name(vault_name);
vault.set_id("2");
InstanceInfoPB instance;
instance.add_storage_vault_names(vault.name());
instance.add_resource_ids(vault.id());
instance.set_instance_id("GetObjStoreInfoTestInstance");
instance.set_enable_storage_vault(true);
val = instance.SerializeAsString();
txn->put(key, val);
txn->put(storage_vault_key({instance.instance_id(), "2"}), vault.SerializeAsString());
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
txn = nullptr;
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_S3_VAULT);
StorageVaultPB vault;
vault.mutable_obj_info()->set_endpoint("s3.us-east-1.amazonaws.com");
vault.mutable_obj_info()->set_region("us-east-1");
vault.mutable_obj_info()->set_bucket("test_bucket");
vault.mutable_obj_info()->set_prefix("test_prefix");
vault.mutable_obj_info()->set_ak("new_ak");
vault.mutable_obj_info()->set_sk("new_sk");
vault.mutable_obj_info()->set_provider(
ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
vault.set_name("ak_sk_s3_vault");
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
{
InstanceInfoPB instance;
get_test_instance(instance);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string val;
ASSERT_EQ(txn->get(storage_vault_key({instance.instance_id(), "2"}), &val),
TxnErrorCode::TXN_OK);
StorageVaultPB get_obj;
get_obj.ParseFromString(val);
ASSERT_EQ(get_obj.obj_info().ak(), "ak") << get_obj.obj_info().ak();
}
{
InstanceInfoPB instance;
get_test_instance(instance);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string val;
ASSERT_EQ(txn->get(storage_vault_key({instance.instance_id(), "3"}), &val),
TxnErrorCode::TXN_OK);
StorageVaultPB get_obj;
get_obj.ParseFromString(val);
ASSERT_EQ(get_obj.obj_info().ak(), "new_ak") << get_obj.obj_info().ak();
ASSERT_NE(get_obj.obj_info().sk(), "new_sk") << get_obj.obj_info().sk();
}
}
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_S3_VAULT);
StorageVaultPB vault;
vault.mutable_obj_info()->set_endpoint("s3.us-east-1.amazonaws.com");
vault.mutable_obj_info()->set_region("us-east-1");
vault.mutable_obj_info()->set_bucket("test_bucket");
vault.mutable_obj_info()->set_prefix("test_prefix");
vault.mutable_obj_info()->set_role_arn("arn:aws:iam::123456789012:role/test-role");
vault.mutable_obj_info()->set_external_id("external_id");
vault.mutable_obj_info()->set_provider(
ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
vault.mutable_obj_info()->set_cred_provider_type(CredProviderTypePB::INSTANCE_PROFILE);
vault.set_name("ak_sk_s3_vault_with_role");
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
{
InstanceInfoPB instance;
get_test_instance(instance);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string val;
ASSERT_EQ(txn->get(storage_vault_key({instance.instance_id(), "4"}), &val),
TxnErrorCode::TXN_OK);
StorageVaultPB get_obj;
get_obj.ParseFromString(val);
ASSERT_EQ(get_obj.obj_info().ak().empty(), true) << get_obj.obj_info().ak();
ASSERT_EQ(get_obj.obj_info().sk().empty(), true) << get_obj.obj_info().sk();
ASSERT_EQ(get_obj.obj_info().role_arn(), "arn:aws:iam::123456789012:role/test-role")
<< get_obj.obj_info().role_arn();
ASSERT_EQ(get_obj.obj_info().external_id(), "external_id")
<< get_obj.obj_info().external_id();
ASSERT_EQ(get_obj.obj_info().endpoint(), "s3.us-east-1.amazonaws.com")
<< get_obj.obj_info().endpoint();
ASSERT_EQ(get_obj.obj_info().region(), "us-east-1") << get_obj.obj_info().region();
ASSERT_EQ(get_obj.obj_info().bucket(), "test_bucket") << get_obj.obj_info().bucket();
ASSERT_EQ(get_obj.obj_info().prefix(), "test_prefix") << get_obj.obj_info().prefix();
ASSERT_EQ(get_obj.obj_info().provider(),
ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3)
<< get_obj.obj_info().provider();
ASSERT_EQ(get_obj.name(), "ak_sk_s3_vault_with_role") << get_obj.name();
ASSERT_EQ(get_obj.id(), "4") << get_obj.id();
ASSERT_EQ(get_obj.obj_info().cred_provider_type(), CredProviderTypePB::INSTANCE_PROFILE)
<< get_obj.obj_info().cred_provider_type();
}
}
LOG(INFO) << "instance:" << instance.ShortDebugString();
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, AddObjInfoWithRole) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
InstanceInfoPB instance;
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ADD_OBJ_INFO);
auto sp = SyncPoint::get_instance();
sp->set_call_back("create_object_info_with_encrypt", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
});
sp->enable_processing();
ObjectStoreInfoPB obj_info;
obj_info.set_endpoint("s3.us-east-1.amazonaws.com");
obj_info.set_region("us-east-1");
obj_info.set_bucket("test_bucket");
obj_info.set_prefix("test_prefix");
obj_info.set_role_arn("arn:aws:iam::123456789012:role/test-role");
obj_info.set_external_id("external_id");
obj_info.set_provider(ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
obj_info.set_cred_provider_type(CredProviderTypePB::INSTANCE_PROFILE);
req.mutable_obj()->MergeFrom(obj_info);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
const auto& obj = instance.obj_info().at(0);
ASSERT_EQ(obj.id(), "1");
ASSERT_EQ(obj.ak().empty(), true) << obj.ak();
ASSERT_EQ(obj.sk().empty(), true) << obj.sk();
ASSERT_EQ(obj.role_arn(), "arn:aws:iam::123456789012:role/test-role") << obj.role_arn();
ASSERT_EQ(obj.external_id(), "external_id") << obj.external_id();
ASSERT_EQ(obj.endpoint(), "s3.us-east-1.amazonaws.com") << obj.endpoint();
ASSERT_EQ(obj.region(), "us-east-1") << obj.region();
ASSERT_EQ(obj.bucket(), "test_bucket") << obj.bucket();
ASSERT_EQ(obj.prefix(), "test_prefix") << obj.prefix();
ASSERT_EQ(obj.provider(), ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3)
<< obj.provider();
ASSERT_EQ(obj.cred_provider_type(), CredProviderTypePB::INSTANCE_PROFILE)
<< obj.cred_provider_type();
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, CheckJobExisted) {
auto meta_service = get_meta_service();
std::string instance_id = "check_job_existed_instance_id";
auto sp = SyncPoint::get_instance();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
};
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->enable_processing();
// OK
{
constexpr auto table_id = 952701, index_id = 952702, partition_id = 952703,
tablet_id = 952704;
int64_t txn_id = 952705;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
{
StartTabletJobResponse res;
start_compaction_job(meta_service.get(), tablet_id, "compaction1", "ip:port", 0, 0,
TabletCompactionJobPB::BASE, res);
}
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->set_tablet_job_id("compaction1");
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->prepare_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
res.Clear();
}
// job does not exist,
{
constexpr auto table_id = 952801, index_id = 952802, partition_id = 952803,
tablet_id = 952804;
int64_t txn_id = 952805;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->set_tablet_job_id("compaction1");
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->prepare_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
ASSERT_EQ(res.status().code(), MetaServiceCode::STALE_PREPARE_ROWSET) << res.status().msg();
res.Clear();
}
// compaction job exists, job id not match
{
constexpr auto table_id = 952901, index_id = 952902, partition_id = 952903,
tablet_id = 952904;
int64_t txn_id = 952905;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
{
StartTabletJobResponse res;
start_compaction_job(meta_service.get(), tablet_id, "compaction1", "ip:port", 0, 0,
TabletCompactionJobPB::BASE, res);
}
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->set_tablet_job_id("compaction2");
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->prepare_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
ASSERT_EQ(res.status().code(), MetaServiceCode::STALE_PREPARE_ROWSET) << res.status().msg();
res.Clear();
}
// do not set job id
{
constexpr auto table_id = 953501, index_id = 953502, partition_id = 953503,
tablet_id = 953504;
int64_t txn_id = 953505;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
{
StartTabletJobResponse res;
start_compaction_job(meta_service.get(), tablet_id, "compaction1", "ip:port", 0, 0,
TabletCompactionJobPB::BASE, res);
}
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->prepare_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
res.Clear();
}
// job id is empty string
{
constexpr auto table_id = 953601, index_id = 953602, partition_id = 953603,
tablet_id = 953604;
int64_t txn_id = 953605;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
{
StartTabletJobResponse res;
start_compaction_job(meta_service.get(), tablet_id, "compaction1", "ip:port", 0, 0,
TabletCompactionJobPB::BASE, res);
}
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->set_tablet_job_id("");
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->prepare_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
res.Clear();
}
// commit rowset OK
{
constexpr auto table_id = 953001, index_id = 953002, partition_id = 953003,
tablet_id = 953004;
int64_t txn_id = 953005;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
{
StartTabletJobResponse res;
start_compaction_job(meta_service.get(), tablet_id, "compaction1", "ip:port", 0, 0,
TabletCompactionJobPB::BASE, res);
}
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->set_tablet_job_id("compaction1");
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->commit_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
res.Clear();
}
// commit rowset, job does not exist,
{
constexpr auto table_id = 953101, index_id = 953102, partition_id = 953103,
tablet_id = 953104;
int64_t txn_id = 952805;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->set_tablet_job_id("compaction1");
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->commit_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
ASSERT_EQ(res.status().code(), MetaServiceCode::STALE_PREPARE_ROWSET) << res.status().msg();
res.Clear();
}
// commit rowset, compaction job exists, job id not match
{
constexpr auto table_id = 953201, index_id = 953202, partition_id = 953203,
tablet_id = 953204;
int64_t txn_id = 952905;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
{
StartTabletJobResponse res;
start_compaction_job(meta_service.get(), tablet_id, "compaction1", "ip:port", 0, 0,
TabletCompactionJobPB::BASE, res);
}
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->set_tablet_job_id("compaction2");
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->commit_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
ASSERT_EQ(res.status().code(), MetaServiceCode::STALE_PREPARE_ROWSET) << res.status().msg();
res.Clear();
}
// do not set job id when commit rowset
{
constexpr auto table_id = 953301, index_id = 953302, partition_id = 953303,
tablet_id = 953304;
int64_t txn_id = 953305;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
{
StartTabletJobResponse res;
start_compaction_job(meta_service.get(), tablet_id, "compaction1", "ip:port", 0, 0,
TabletCompactionJobPB::BASE, res);
}
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->commit_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
res.Clear();
}
// job id is empty string when commit rowset
{
constexpr auto table_id = 953401, index_id = 953402, partition_id = 953403,
tablet_id = 953404;
int64_t txn_id = 953405;
std::string label = "update_rowset_meta_test_label1";
CreateRowsetResponse res;
ASSERT_NO_FATAL_FAILURE(
create_tablet(meta_service.get(), table_id, index_id, partition_id, tablet_id));
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
{
StartTabletJobResponse res;
start_compaction_job(meta_service.get(), tablet_id, "compaction1", "ip:port", 0, 0,
TabletCompactionJobPB::BASE, res);
}
brpc::Controller cntl;
auto arena = res.GetArena();
auto req = google::protobuf::Arena::CreateMessage<CreateRowsetRequest>(arena);
req->set_tablet_job_id("");
req->mutable_rowset_meta()->CopyFrom(rowset);
meta_service->commit_rowset(&cntl, req, &res, nullptr);
if (!arena) delete req;
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
res.Clear();
}
}
TEST(MetaServiceTest, StalePrepareRowset) {
auto meta_service = get_meta_service();
int64_t table_id = 1;
int64_t partition_id = 1;
int64_t tablet_id = 1;
int64_t db_id = 100201;
std::string label = "test_prepare_rowset";
create_tablet(meta_service.get(), table_id, 1, partition_id, tablet_id);
int64_t txn_id = 0;
ASSERT_NO_FATAL_FAILURE(begin_txn(meta_service.get(), db_id, label, table_id, txn_id));
CreateRowsetResponse res;
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
rowset.mutable_load_id()->set_hi(123);
rowset.mutable_load_id()->set_lo(456);
prepare_rowset(meta_service.get(), rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
res.Clear();
ASSERT_NO_FATAL_FAILURE(commit_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
prepare_rowset(meta_service.get(), rowset, res);
ASSERT_TRUE(res.status().msg().find("rowset already exists") != std::string::npos)
<< res.status().msg();
ASSERT_EQ(res.status().code(), MetaServiceCode::ALREADY_EXISTED) << res.status().code();
commit_txn(meta_service.get(), db_id, txn_id, label);
prepare_rowset(meta_service.get(), rowset, res);
ASSERT_TRUE(res.status().msg().find("txn is not in") != std::string::npos)
<< res.status().msg();
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT) << res.status().code();
}
TEST(MetaServiceTest, StaleCommitRowset) {
auto meta_service = get_meta_service();
int64_t table_id = 1;
int64_t partition_id = 1;
int64_t tablet_id = 1;
int64_t db_id = 100201;
std::string label = "test_prepare_rowset";
create_tablet(meta_service.get(), table_id, 1, partition_id, tablet_id);
int64_t txn_id = 0;
ASSERT_NO_FATAL_FAILURE(begin_txn(meta_service.get(), db_id, label, table_id, txn_id));
CreateRowsetResponse res;
auto rowset = create_rowset(txn_id, tablet_id, partition_id);
rowset.mutable_load_id()->set_hi(123);
rowset.mutable_load_id()->set_lo(456);
prepare_rowset(meta_service.get(), rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
res.Clear();
ASSERT_NO_FATAL_FAILURE(commit_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
ASSERT_NO_FATAL_FAILURE(commit_rowset(meta_service.get(), rowset, res));
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << label;
commit_txn(meta_service.get(), db_id, txn_id, label);
ASSERT_NO_FATAL_FAILURE(commit_rowset(meta_service.get(), rowset, res));
ASSERT_TRUE(res.status().msg().find("txn is not in") != std::string::npos)
<< res.status().msg();
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT) << res.status().code();
}
TEST(MetaServiceTest, AlterObjInfoTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ObjectStoreInfoPB obj_info;
obj_info.set_id("1");
obj_info.set_ak("access_key_132131");
obj_info.set_sk("secret_key_434124");
obj_info.set_provider(ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
InstanceInfoPB instance;
instance.add_obj_info()->CopyFrom(obj_info);
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
std::string cipher_sk = "JUkuTDctR+ckJtnPkLScWaQZRcOtWBhsLLpnCRxQLxr734qB8cs6gNLH6grE1FxO";
std::string plain_sk = "Hx60p12123af234541nsVsffdfsdfghsdfhsdf34t";
// update failed
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_OBJ_INFO);
req.mutable_obj()->set_id("2");
req.mutable_obj()->set_ak("new_ak");
req.mutable_obj()->set_sk(plain_sk);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
InstanceInfoPB instance;
get_test_instance(instance);
ASSERT_EQ(instance.obj_info(0).id(), "1");
ASSERT_EQ(instance.obj_info(0).ak(), "access_key_132131");
ASSERT_EQ(instance.obj_info(0).sk(), "secret_key_434124");
}
// update ak/sk successful
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_OBJ_INFO);
req.mutable_obj()->set_id("1");
req.mutable_obj()->set_ak("new_access_key_132131");
req.mutable_obj()->set_sk(plain_sk);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
InstanceInfoPB instance;
get_test_instance(instance);
LOG(INFO) << "instance:" << instance.ShortDebugString();
ASSERT_EQ(instance.obj_info(0).id(), "1");
ASSERT_EQ(instance.obj_info(0).ak(), "new_access_key_132131");
ASSERT_EQ(instance.obj_info(0).sk(), cipher_sk);
}
// update from ak/sk to role_arn
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_OBJ_INFO);
req.mutable_obj()->set_id("1");
req.mutable_obj()->set_role_arn("arn:aws:iam::1453123012:role/test-role");
req.mutable_obj()->set_external_id("external_id_13123");
req.mutable_obj()->set_cred_provider_type(CredProviderTypePB::INSTANCE_PROFILE);
req.mutable_obj()->set_provider(ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
InstanceInfoPB instance;
get_test_instance(instance);
LOG(INFO) << "instance:" << instance.ShortDebugString();
ASSERT_EQ(instance.obj_info(0).id(), "1");
ASSERT_EQ(instance.obj_info(0).role_arn(), "arn:aws:iam::1453123012:role/test-role");
ASSERT_EQ(instance.obj_info(0).external_id(), "external_id_13123");
ASSERT_EQ(instance.obj_info(0).provider(),
ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
ASSERT_EQ(instance.obj_info(0).cred_provider_type(), CredProviderTypePB::INSTANCE_PROFILE);
ASSERT_TRUE(instance.obj_info(0).ak().empty());
ASSERT_TRUE(instance.obj_info(0).sk().empty());
ASSERT_FALSE(instance.obj_info(0).has_encryption_info());
}
// update from role_arn to ak/sk
{
AlterObjStoreInfoRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_OBJ_INFO);
req.mutable_obj()->set_id("1");
req.mutable_obj()->set_ak("new_access_key_132131");
req.mutable_obj()->set_sk(plain_sk);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_obj_store_info(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
InstanceInfoPB instance;
get_test_instance(instance);
LOG(INFO) << "instance:" << instance.ShortDebugString();
ASSERT_EQ(instance.obj_info(0).id(), "1");
ASSERT_EQ(instance.obj_info(0).ak(), "new_access_key_132131");
ASSERT_EQ(instance.obj_info(0).sk(), cipher_sk);
ASSERT_EQ(instance.obj_info(0).provider(),
ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
ASSERT_FALSE(instance.obj_info(0).has_cred_provider_type());
ASSERT_FALSE(instance.obj_info(0).has_role_arn());
ASSERT_FALSE(instance.obj_info(0).has_external_id());
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
TEST(MetaServiceTest, AlterS3StorageVaultWithRoleArnTest) {
auto meta_service = get_meta_service();
auto sp = SyncPoint::get_instance();
sp->enable_processing();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "selectdbselectdbselectdbselectdb";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
std::pair<std::string, std::string> pair;
sp->set_call_back("extract_object_storage_info:get_aksk_pair", [&](auto&& args) {
auto* ret = try_any_cast<std::pair<std::string, std::string>*>(args[0]);
pair = *ret;
});
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string key;
std::string val;
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ObjectStoreInfoPB obj_info;
obj_info.set_id("1");
obj_info.set_ak("123456ab");
obj_info.set_sk("@ak$");
obj_info.set_provider(ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
StorageVaultPB vault;
constexpr char vault_name[] = "test_alter_s3_vault_111";
vault.mutable_obj_info()->MergeFrom(obj_info);
vault.set_name(vault_name);
vault.set_id("2");
InstanceInfoPB instance;
instance.add_storage_vault_names(vault.name());
instance.add_resource_ids(vault.id());
instance.set_instance_id("GetObjStoreInfoTestInstance");
val = instance.SerializeAsString();
txn->put(key, val);
txn->put(storage_vault_key({instance.instance_id(), "2"}), vault.SerializeAsString());
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
txn = nullptr;
auto get_test_instance = [&](InstanceInfoPB& i) {
std::string key;
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
InstanceKeyInfo key_info {"test_instance"};
instance_key(key_info, &key);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
i.ParseFromString(val);
};
// update from ak/sk to role_arn
{
AlterObjStoreInfoRequest req;
constexpr char new_vault_name[] = "new_test_alter_s3_vault_111";
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_S3_VAULT);
StorageVaultPB vault;
vault.set_alter_name(new_vault_name);
ObjectStoreInfoPB obj;
obj.set_role_arn("arn:aws:iam::12311321:role/test-alter-role");
obj.set_external_id("external_id_123123");
vault.mutable_obj_info()->MergeFrom(obj);
vault.set_name(vault_name);
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
LOG(INFO) << "instance:" << instance.ShortDebugString();
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string val;
ASSERT_EQ(txn->get(storage_vault_key({instance.instance_id(), "2"}), &val),
TxnErrorCode::TXN_OK);
StorageVaultPB get_obj;
get_obj.ParseFromString(val);
ASSERT_EQ(get_obj.id(), "2");
ASSERT_EQ(get_obj.obj_info().role_arn(), "arn:aws:iam::12311321:role/test-alter-role");
ASSERT_EQ(get_obj.obj_info().external_id(), "external_id_123123");
ASSERT_EQ(get_obj.obj_info().provider(),
ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
ASSERT_EQ(get_obj.obj_info().cred_provider_type(), CredProviderTypePB::INSTANCE_PROFILE);
ASSERT_TRUE(get_obj.obj_info().ak().empty());
ASSERT_TRUE(get_obj.obj_info().sk().empty());
ASSERT_FALSE(get_obj.obj_info().has_encryption_info());
ASSERT_EQ(get_obj.name(), new_vault_name) << get_obj.obj_info().ShortDebugString();
}
std::string cipher_sk = "JUkuTDctR+ckJtnPkLScWaQZRcOtWBhsLLpnCRxQLxr734qB8cs6gNLH6grE1FxO";
std::string plain_sk = "Hx60p12123af234541nsVsffdfsdfghsdfhsdf34t";
// update from role_arn to ak_sk
{
AlterObjStoreInfoRequest req;
constexpr char new_vault_name[] = "new_test_alter_s3_vault_111";
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_op(AlterObjStoreInfoRequest::ALTER_S3_VAULT);
StorageVaultPB vault;
ObjectStoreInfoPB obj;
obj.set_ak("123456ab");
obj.set_sk(plain_sk);
vault.mutable_obj_info()->MergeFrom(obj);
vault.set_name(new_vault_name);
req.mutable_vault()->CopyFrom(vault);
brpc::Controller cntl;
AlterObjStoreInfoResponse res;
meta_service->alter_storage_vault(
reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
InstanceInfoPB instance;
get_test_instance(instance);
LOG(INFO) << "instance:" << instance.ShortDebugString();
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
std::string val;
ASSERT_EQ(txn->get(storage_vault_key({instance.instance_id(), "2"}), &val),
TxnErrorCode::TXN_OK);
StorageVaultPB get_obj;
get_obj.ParseFromString(val);
ASSERT_EQ(get_obj.id(), "2");
ASSERT_EQ(get_obj.obj_info().provider(),
ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
ASSERT_EQ(get_obj.obj_info().ak(), "123456ab");
ASSERT_EQ(get_obj.obj_info().sk(), cipher_sk);
ASSERT_TRUE(get_obj.obj_info().role_arn().empty());
ASSERT_TRUE(get_obj.obj_info().external_id().empty());
ASSERT_TRUE(get_obj.obj_info().has_encryption_info());
ASSERT_FALSE(get_obj.obj_info().has_cred_provider_type());
ASSERT_EQ(get_obj.name(), new_vault_name) << get_obj.obj_info().ShortDebugString();
}
SyncPoint::get_instance()->disable_processing();
SyncPoint::get_instance()->clear_all_call_backs();
}
void scan_restore_job_rowset(
Transaction* txn, const std::string& instance_id, int64_t tablet_id, MetaServiceCode& code,
std::string& msg,
std::vector<std::pair<std::string, doris::RowsetMetaCloudPB>>* restore_job_rs_metas);
TEST(MetaServiceTest, RestoreJobTest) {
auto meta_service = get_meta_service();
ASSERT_NE(meta_service, nullptr);
std::string instance_id = "test_prepare_restore_job_instance_id";
auto sp = SyncPoint::get_instance();
std::unique_ptr<int, std::function<void(int*)>> defer(
(int*)0x01, [](int*) { SyncPoint::get_instance()->clear_all_call_backs(); });
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = instance_id;
ret->second = true;
});
sp->enable_processing();
auto reset_meta_service = [&meta_service] { meta_service = get_meta_service(); };
constexpr int64_t table_id = 10001;
constexpr int64_t index_id = 10002;
constexpr int64_t partition_id = 10003;
constexpr int64_t tablet_id = 10004;
constexpr int64_t version = 1;
constexpr int64_t txn_id = 0;
TabletIndexPB tablet_idx;
tablet_idx.set_table_id(table_id);
tablet_idx.set_index_id(index_id);
tablet_idx.set_partition_id(partition_id);
tablet_idx.set_tablet_id(tablet_id);
std::string tablet_idx_val;
tablet_idx.SerializeToString(&tablet_idx_val);
std::unique_ptr<Transaction> txn;
brpc::Controller cntl;
RestoreJobRequest req;
RestoreJobResponse res;
// ------------Test prepare restore job------------
// invalid args prepare restore job
{
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(meta_tablet_idx_key({instance_id, tablet_id}), tablet_idx_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
// empty action
meta_service->prepare_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_TRUE(res.status().msg().find("invalid action") != std::string::npos);
req.set_action(RestoreJobRequest::PREPARE);
// empty tablet id
meta_service->prepare_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(res.status().msg(), "empty tablet_id");
// restore with empty tablet meta
req.set_tablet_id(tablet_id);
res.Clear();
meta_service->prepare_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(res.status().msg(), "no tablet meta");
// check key existence
std::string restore_job_key = job_restore_tablet_key({instance_id, tablet_id});
std::string val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
req.Clear();
res.Clear();
}
// normal prepare restore job
{
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(meta_tablet_idx_key({instance_id, tablet_id}), tablet_idx_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
req.set_tablet_id(tablet_id);
req.set_expiration(time(nullptr) + 3600);
req.set_action(RestoreJobRequest::PREPARE);
// set tablet meta
auto* tablet_meta = req.mutable_tablet_meta();
tablet_meta->set_table_id(table_id);
tablet_meta->set_index_id(index_id);
tablet_meta->set_partition_id(partition_id);
tablet_meta->set_tablet_id(tablet_id);
tablet_meta->set_schema_version(1);
auto* rs_meta = tablet_meta->add_rs_metas();
*rs_meta = create_rowset(txn_id, tablet_id, partition_id, version);
meta_service->prepare_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
std::string restore_job_key = job_restore_tablet_key({instance_id, tablet_id});
std::string val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
RestoreJobCloudPB restore_job_pb;
ASSERT_TRUE(restore_job_pb.ParseFromString(val));
ASSERT_EQ(restore_job_pb.tablet_id(), tablet_id);
ASSERT_EQ(restore_job_pb.state(), RestoreJobCloudPB::PREPARED);
ASSERT_EQ(restore_job_pb.tablet_meta().schema_version(), 1);
std::string restore_job_rs_key = job_restore_rowset_key({instance_id, tablet_id, version});
ASSERT_EQ(txn->get(restore_job_rs_key, &val), TxnErrorCode::TXN_OK);
RowsetMetaCloudPB rs_meta_pb;
ASSERT_TRUE(rs_meta_pb.ParseFromString(val));
ASSERT_EQ(rs_meta_pb.tablet_id(), tablet_id);
ASSERT_EQ(rs_meta_pb.rowset_id_v2(), rs_meta->rowset_id_v2());
req.Clear();
res.Clear();
}
// duplicate prepare restore job
{
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(meta_tablet_idx_key({instance_id, tablet_id}), tablet_idx_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
req.set_tablet_id(tablet_id);
req.set_action(RestoreJobRequest::PREPARE);
// set tablet meta
auto* tablet_meta = req.mutable_tablet_meta();
tablet_meta->set_table_id(table_id);
tablet_meta->set_index_id(index_id);
tablet_meta->set_partition_id(partition_id);
tablet_meta->set_tablet_id(tablet_id);
tablet_meta->set_schema_version(1);
auto* rs_meta = tablet_meta->add_rs_metas();
*rs_meta = create_rowset(txn_id, tablet_id, partition_id, version);
// first request
meta_service->prepare_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
std::string restore_job_key = job_restore_tablet_key({instance_id, tablet_id});
std::string val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
RestoreJobCloudPB restore_job_pb;
ASSERT_TRUE(restore_job_pb.ParseFromString(val));
ASSERT_EQ(restore_job_pb.tablet_id(), tablet_id);
ASSERT_EQ(restore_job_pb.state(), RestoreJobCloudPB::PREPARED);
ASSERT_EQ(restore_job_pb.version(), 0);
// second request
meta_service->prepare_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
restore_job_pb.Clear();
ASSERT_TRUE(restore_job_pb.ParseFromString(val));
ASSERT_EQ(restore_job_pb.tablet_id(), tablet_id);
ASSERT_EQ(restore_job_pb.state(), RestoreJobCloudPB::PREPARED);
ASSERT_EQ(restore_job_pb.version(), 1);
req.Clear();
res.Clear();
}
// ------------Test commit restore job------------
// invalid args commit restore job
{
reset_meta_service();
// empty action
meta_service->commit_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_TRUE(res.status().msg().find("invalid action") != std::string::npos);
req.set_action(RestoreJobRequest::COMMIT);
// empty tablet_id
meta_service->commit_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(res.status().msg(), "empty tablet_id");
// check key existence
std::string restore_job_key = job_restore_tablet_key({instance_id, tablet_id});
std::string val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
req.Clear();
res.Clear();
}
// commit restore job not exits
{
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(meta_tablet_idx_key({instance_id, tablet_id}), tablet_idx_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
req.set_tablet_id(tablet_id);
req.set_action(RestoreJobRequest::COMMIT);
meta_service->commit_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(res.status().msg(), "restore job not exists or has been recycled");
req.Clear();
res.Clear();
}
// normal commit restore job
for (int store_version = 0; store_version < 4; store_version++) {
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(meta_tablet_idx_key({instance_id, tablet_id}), tablet_idx_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
// prepare restore job
RestoreJobRequest make_req;
RestoreJobResponse make_res;
make_req.set_tablet_id(tablet_id);
make_req.set_expiration(time(nullptr) + 3600);
make_req.set_action(RestoreJobRequest::PREPARE);
auto* tablet_meta = make_req.mutable_tablet_meta();
tablet_meta->set_table_id(table_id);
tablet_meta->set_index_id(index_id);
tablet_meta->set_partition_id(partition_id);
tablet_meta->set_tablet_id(tablet_id);
tablet_meta->set_schema_version(1);
auto* rs_meta = tablet_meta->add_rs_metas();
*rs_meta = create_rowset(txn_id, tablet_id, partition_id, version);
auto* delete_bitmap = tablet_meta->mutable_delete_bitmap();
// 1
delete_bitmap->add_rowset_ids(rs_meta->rowset_id_v2());
delete_bitmap->add_versions(1);
delete_bitmap->add_segment_ids(1);
delete_bitmap->add_segment_delete_bitmaps("test_bitmap");
// 2
delete_bitmap->add_rowset_ids(rs_meta->rowset_id_v2());
delete_bitmap->add_versions(1);
delete_bitmap->add_segment_ids(2);
delete_bitmap->add_segment_delete_bitmaps("test_bitmap2");
// 3
delete_bitmap->add_rowset_ids(rs_meta->rowset_id_v2() + "2");
delete_bitmap->add_versions(1);
delete_bitmap->add_segment_ids(1);
delete_bitmap->add_segment_delete_bitmaps("test_bitmap3");
meta_service->prepare_restore_job(&cntl, &make_req, &make_res, nullptr);
ASSERT_EQ(make_res.status().code(), MetaServiceCode::OK);
std::string restore_job_key = job_restore_tablet_key({instance_id, tablet_id});
std::string val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
std::string restore_job_rs_key = job_restore_rowset_key({instance_id, tablet_id, version});
ASSERT_EQ(txn->get(restore_job_rs_key, &val), TxnErrorCode::TXN_OK);
// commit_restore_job
req.set_tablet_id(tablet_id);
req.set_action(RestoreJobRequest::COMMIT);
if (store_version > 0) {
req.set_store_version(store_version);
}
meta_service->commit_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
std::string tablet_key =
meta_tablet_key({instance_id, table_id, index_id, partition_id, tablet_id});
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(tablet_key, &val), TxnErrorCode::TXN_OK);
TabletMetaCloudPB saved_tablet_meta;
ASSERT_TRUE(saved_tablet_meta.ParseFromString(val));
ASSERT_EQ(saved_tablet_meta.tablet_id(), tablet_id);
ASSERT_EQ(saved_tablet_meta.schema_version(), 1);
std::string rs_key = meta_rowset_key({instance_id, tablet_id, version});
ASSERT_EQ(txn->get(rs_key, &val), TxnErrorCode::TXN_OK);
RowsetMetaCloudPB saved_rs_meta;
ASSERT_TRUE(saved_rs_meta.ParseFromString(val));
ASSERT_EQ(saved_rs_meta.tablet_id(), tablet_id);
ASSERT_EQ(saved_rs_meta.rowset_id_v2(), rs_meta->rowset_id_v2());
// check delete bitmap
{
TxnErrorCode err = (store_version == 0 || store_version == 1 || store_version == 3)
? TxnErrorCode::TXN_OK
: TxnErrorCode::TXN_KEY_NOT_FOUND;
std::string bitmap_key =
meta_delete_bitmap_key({instance_id, tablet_id, rs_meta->rowset_id_v2(), 1, 1});
ASSERT_EQ(txn->get(bitmap_key, &val), err);
if (err == TxnErrorCode::TXN_OK) {
ASSERT_EQ(val, "test_bitmap");
}
bitmap_key =
meta_delete_bitmap_key({instance_id, tablet_id, rs_meta->rowset_id_v2(), 1, 2});
ASSERT_EQ(txn->get(bitmap_key, &val), err);
if (err == TxnErrorCode::TXN_OK) {
ASSERT_EQ(val, "test_bitmap2");
}
bitmap_key = meta_delete_bitmap_key(
{instance_id, tablet_id, rs_meta->rowset_id_v2() + "2", 1, 1});
ASSERT_EQ(txn->get(bitmap_key, &val), err);
if (err == TxnErrorCode::TXN_OK) {
ASSERT_EQ(val, "test_bitmap3");
}
}
{
TxnErrorCode err = (store_version == 2 || store_version == 3)
? TxnErrorCode::TXN_OK
: TxnErrorCode::TXN_KEY_NOT_FOUND;
std::string bitmap_key = versioned::meta_delete_bitmap_key(
{instance_id, tablet_id, rs_meta->rowset_id_v2()});
ValueBuf val_buf;
ASSERT_EQ(cloud::blob_get(txn.get(), bitmap_key, &val_buf), err);
if (err == TxnErrorCode::TXN_OK) {
DeleteBitmapStoragePB delete_bitmap_storage;
ASSERT_TRUE(val_buf.to_pb(&delete_bitmap_storage));
ASSERT_TRUE(delete_bitmap_storage.store_in_fdb());
auto& delete_bitmap_pb = delete_bitmap_storage.delete_bitmap();
ASSERT_EQ(delete_bitmap_pb.rowset_ids_size(), 2);
ASSERT_EQ(delete_bitmap_pb.rowset_ids(0), rs_meta->rowset_id_v2());
ASSERT_EQ(delete_bitmap_pb.segment_ids(0), 1);
ASSERT_EQ(delete_bitmap_pb.versions(0), 1);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(0), "test_bitmap");
ASSERT_EQ(delete_bitmap_pb.rowset_ids(1), rs_meta->rowset_id_v2());
ASSERT_EQ(delete_bitmap_pb.segment_ids(1), 2);
ASSERT_EQ(delete_bitmap_pb.versions(1), 1);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(1), "test_bitmap2");
}
bitmap_key = versioned::meta_delete_bitmap_key(
{instance_id, tablet_id, rs_meta->rowset_id_v2() + "2"});
ASSERT_EQ(cloud::blob_get(txn.get(), bitmap_key, &val_buf), err);
if (err == TxnErrorCode::TXN_OK) {
DeleteBitmapStoragePB delete_bitmap_storage;
ASSERT_TRUE(val_buf.to_pb(&delete_bitmap_storage));
ASSERT_TRUE(delete_bitmap_storage.store_in_fdb());
auto& delete_bitmap_pb = delete_bitmap_storage.delete_bitmap();
ASSERT_EQ(delete_bitmap_pb.rowset_ids_size(), 1);
ASSERT_EQ(delete_bitmap_pb.rowset_ids(0), rs_meta->rowset_id_v2() + "2");
ASSERT_EQ(delete_bitmap_pb.segment_ids(0), 1);
ASSERT_EQ(delete_bitmap_pb.versions(0), 1);
ASSERT_EQ(delete_bitmap_pb.segment_delete_bitmaps(0), "test_bitmap3");
}
}
// ths restore job key should not be removed, restore job rowset key should be found
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
std::vector<std::pair<std::string, doris::RowsetMetaCloudPB>> restore_job_rs_metas;
MetaServiceCode code;
std::string msg;
scan_restore_job_rowset(txn.get(), instance_id, tablet_id, code, msg,
&restore_job_rs_metas);
ASSERT_EQ(code, MetaServiceCode::OK) << msg;
ASSERT_EQ(restore_job_rs_metas.size(), 1);
req.Clear();
res.Clear();
}
// large commit restore job request with 10000 rowset meta
{
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(meta_tablet_idx_key({instance_id, tablet_id}), tablet_idx_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
// prepare restore job
RestoreJobRequest make_req;
RestoreJobResponse make_res;
make_req.set_tablet_id(tablet_id);
make_req.set_expiration(time(nullptr) + 3600);
make_req.set_action(RestoreJobRequest::PREPARE);
auto* tablet_meta = make_req.mutable_tablet_meta();
tablet_meta->set_table_id(table_id);
tablet_meta->set_index_id(index_id);
tablet_meta->set_partition_id(partition_id);
tablet_meta->set_tablet_id(tablet_id);
tablet_meta->set_schema_version(1);
// add 10000 rowset meta
constexpr int LARGE_ROWSET_COUNT = 10000;
for (int64_t ver = 1; ver <= LARGE_ROWSET_COUNT; ver++) {
auto* rs_meta = tablet_meta->add_rs_metas();
*rs_meta = create_rowset(txn_id, tablet_id, partition_id, ver);
}
meta_service->prepare_restore_job(&cntl, &make_req, &make_res, nullptr);
ASSERT_EQ(make_res.status().code(), MetaServiceCode::OK) << make_res.status().msg();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
for (int64_t ver = 1; ver <= LARGE_ROWSET_COUNT; ver++) {
std::string restore_job_rs_key = job_restore_rowset_key({instance_id, tablet_id, ver});
std::string val;
ASSERT_EQ(txn->get(restore_job_rs_key, &val), TxnErrorCode::TXN_OK);
RowsetMetaCloudPB rs_meta_pb;
ASSERT_TRUE(rs_meta_pb.ParseFromString(val));
ASSERT_EQ(rs_meta_pb.tablet_id(), tablet_id);
ASSERT_EQ(rs_meta_pb.start_version(), ver);
ASSERT_EQ(rs_meta_pb.end_version(), ver);
}
// commit_restore_job
req.set_tablet_id(tablet_id);
req.set_action(RestoreJobRequest::COMMIT);
meta_service->commit_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
for (int64_t ver = 1; ver <= LARGE_ROWSET_COUNT; ver++) {
std::string rs_key = meta_rowset_key({instance_id, tablet_id, ver});
std::string val;
ASSERT_EQ(txn->get(rs_key, &val), TxnErrorCode::TXN_OK);
RowsetMetaCloudPB saved_rs_meta;
ASSERT_TRUE(saved_rs_meta.ParseFromString(val));
ASSERT_EQ(saved_rs_meta.tablet_id(), tablet_id);
ASSERT_EQ(saved_rs_meta.start_version(), ver);
ASSERT_EQ(saved_rs_meta.end_version(), ver);
}
// ths restore job key should not be removed, restore job rowset key should be found
std::string restore_job_key = job_restore_tablet_key({instance_id, tablet_id});
std::string val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
std::vector<std::pair<std::string, doris::RowsetMetaCloudPB>> restore_job_rs_metas;
MetaServiceCode code;
std::string msg;
scan_restore_job_rowset(txn.get(), instance_id, tablet_id, code, msg,
&restore_job_rs_metas);
ASSERT_EQ(code, MetaServiceCode::OK) << msg;
ASSERT_EQ(restore_job_rs_metas.size(), 10000);
req.Clear();
res.Clear();
}
// ------------Test finish restore job------------
// invalid args finish restore job
{
reset_meta_service();
// empty action
meta_service->commit_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_TRUE(res.status().msg().find("invalid action") != std::string::npos);
req.set_action(RestoreJobRequest::COMPLETE);
// empty tablet_id
meta_service->finish_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(res.status().msg(), "empty tablet_id");
// check key existence
std::string restore_job_key = job_restore_tablet_key({instance_id, tablet_id});
std::string val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_KEY_NOT_FOUND);
req.Clear();
res.Clear();
}
// finish restore job not exists
{
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(meta_tablet_idx_key({instance_id, tablet_id}), tablet_idx_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
req.set_tablet_id(tablet_id);
req.set_action(RestoreJobRequest::COMPLETE);
meta_service->finish_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_EQ(res.status().msg(), "restore job not exists or has been recycled");
req.Clear();
res.Clear();
}
// finish restore job COMMITTED -> COMPLETED
{
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(meta_tablet_idx_key({instance_id, tablet_id}), tablet_idx_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
// prepare restore job
RestoreJobRequest make_req;
RestoreJobResponse make_res;
make_req.set_tablet_id(tablet_id);
make_req.set_expiration(time(nullptr) + 3600);
make_req.set_action(RestoreJobRequest::PREPARE);
auto* tablet_meta = make_req.mutable_tablet_meta();
tablet_meta->set_table_id(table_id);
tablet_meta->set_index_id(index_id);
tablet_meta->set_partition_id(partition_id);
tablet_meta->set_tablet_id(tablet_id);
tablet_meta->set_schema_version(1);
auto* rs_meta = tablet_meta->add_rs_metas();
*rs_meta = create_rowset(txn_id, tablet_id, partition_id, version);
auto* delete_bitmap = tablet_meta->mutable_delete_bitmap();
delete_bitmap->add_rowset_ids(rs_meta->rowset_id_v2());
delete_bitmap->add_versions(1);
delete_bitmap->add_segment_ids(1);
delete_bitmap->add_segment_delete_bitmaps("test_bitmap");
meta_service->prepare_restore_job(&cntl, &make_req, &make_res, nullptr);
ASSERT_EQ(make_res.status().code(), MetaServiceCode::OK);
std::string restore_job_key = job_restore_tablet_key({instance_id, tablet_id});
std::string val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
std::string restore_job_rs_key = job_restore_rowset_key({instance_id, tablet_id, version});
ASSERT_EQ(txn->get(restore_job_rs_key, &val), TxnErrorCode::TXN_OK);
req.set_tablet_id(tablet_id);
req.set_action(RestoreJobRequest::COMMIT);
meta_service->commit_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_rs_key, &val), TxnErrorCode::TXN_OK);
// finish_restore_job to COMPLETED
req.set_tablet_id(tablet_id);
req.set_action(RestoreJobRequest::COMPLETE);
meta_service->finish_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
// this restore job key should be in COMPLETED state
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
RestoreJobCloudPB restore_job_pb;
ASSERT_TRUE(restore_job_pb.ParseFromString(val));
ASSERT_EQ(restore_job_pb.state(), RestoreJobCloudPB::COMPLETED);
ASSERT_EQ(restore_job_pb.need_recycle_data(), false);
std::vector<std::pair<std::string, doris::RowsetMetaCloudPB>> restore_job_rs_metas;
MetaServiceCode code;
std::string msg;
scan_restore_job_rowset(txn.get(), instance_id, tablet_id, code, msg,
&restore_job_rs_metas);
ASSERT_EQ(code, MetaServiceCode::OK) << msg;
ASSERT_EQ(restore_job_rs_metas.size(), 1);
req.Clear();
res.Clear();
}
// finish restore job state PREPARED -> DROPPED
{
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(meta_tablet_idx_key({instance_id, tablet_id}), tablet_idx_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
// prepare restore job
RestoreJobRequest make_req;
RestoreJobResponse make_res;
make_req.set_tablet_id(tablet_id);
make_req.set_expiration(time(nullptr) + 3600);
make_req.set_action(RestoreJobRequest::PREPARE);
auto* tablet_meta = make_req.mutable_tablet_meta();
tablet_meta->set_table_id(table_id);
tablet_meta->set_index_id(index_id);
tablet_meta->set_partition_id(partition_id);
tablet_meta->set_tablet_id(tablet_id);
tablet_meta->set_schema_version(1);
auto* rs_meta = tablet_meta->add_rs_metas();
*rs_meta = create_rowset(txn_id, tablet_id, partition_id, version);
meta_service->prepare_restore_job(&cntl, &make_req, &make_res, nullptr);
ASSERT_EQ(make_res.status().code(), MetaServiceCode::OK);
std::string restore_job_key = job_restore_tablet_key({instance_id, tablet_id});
std::string val;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
std::string restore_job_rs_key = job_restore_rowset_key({instance_id, tablet_id, version});
ASSERT_EQ(txn->get(restore_job_rs_key, &val), TxnErrorCode::TXN_OK);
// finish_restore_job to DROPPED
req.set_tablet_id(tablet_id);
req.set_action(RestoreJobRequest::ABORT);
meta_service->finish_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK) << res.status().msg();
// this restore job key should be in DROPPED state
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(restore_job_key, &val), TxnErrorCode::TXN_OK);
RestoreJobCloudPB restore_job_pb;
ASSERT_TRUE(restore_job_pb.ParseFromString(val));
ASSERT_EQ(restore_job_pb.state(), RestoreJobCloudPB::DROPPED);
ASSERT_EQ(restore_job_pb.need_recycle_data(), true);
std::vector<std::pair<std::string, doris::RowsetMetaCloudPB>> restore_job_rs_metas;
MetaServiceCode code;
std::string msg;
scan_restore_job_rowset(txn.get(), instance_id, tablet_id, code, msg,
&restore_job_rs_metas);
ASSERT_EQ(code, MetaServiceCode::OK) << msg;
ASSERT_EQ(restore_job_rs_metas.size(), 1);
req.Clear();
res.Clear();
}
// finish restore job invalid state PREPARED -> COMPLETED
{
reset_meta_service();
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
txn->put(meta_tablet_idx_key({instance_id, tablet_id}), tablet_idx_val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
// prepare restore job
RestoreJobRequest make_req;
RestoreJobResponse make_res;
make_req.set_tablet_id(tablet_id);
make_req.set_expiration(time(nullptr) + 3600);
make_req.set_action(RestoreJobRequest::PREPARE);
// set tablet meta
auto* tablet_meta = make_req.mutable_tablet_meta();
tablet_meta->set_table_id(table_id);
tablet_meta->set_index_id(index_id);
tablet_meta->set_partition_id(partition_id);
tablet_meta->set_tablet_id(tablet_id);
tablet_meta->set_schema_version(1);
auto* rs_meta = tablet_meta->add_rs_metas();
*rs_meta = create_rowset(txn_id, tablet_id, partition_id, version);
meta_service->prepare_restore_job(&cntl, &make_req, &make_res, nullptr);
ASSERT_EQ(make_res.status().code(), MetaServiceCode::OK);
// finish_restore_job to COMPLETED
req.set_tablet_id(tablet_id);
req.set_action(RestoreJobRequest::COMPLETE);
meta_service->finish_restore_job(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_TRUE(res.status().msg().find("invalid state to complete") != std::string::npos);
}
}
TEST(MetaServiceTest, SetSnapshotPropertyTest) {
auto meta_service = get_meta_service();
// Create a test instance first
{
brpc::Controller cntl;
CreateInstanceRequest req;
req.set_instance_id("test_snapshot_instance");
req.set_user_id("test_user");
req.set_name("test_name");
auto* sp = SyncPoint::get_instance();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->enable_processing();
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
auto* sp = SyncPoint::get_instance();
sp->set_call_back("notify_refresh_instance_return",
[](auto&& args) { *try_any_cast<bool*>(args.back()) = true; });
sp->enable_processing();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
SyncPoint::get_instance()->disable_processing();
};
// Test case 1: Snapshot not ready (SNAPSHOT_SWITCH_DISABLED) - should fail
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::ENABLE_SNAPSHOT)] = "true";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_TRUE(res.status().msg().find("Snapshot not ready") != std::string::npos);
}
// Initialize snapshot switch status to OFF so we can test snapshot functionality
{
InstanceKeyInfo key_info {"test_snapshot_instance"};
std::string key;
std::string val;
instance_key(key_info, &key);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
InstanceInfoPB instance;
instance.ParseFromString(val);
instance.set_snapshot_switch_status(SNAPSHOT_SWITCH_OFF);
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
}
// Test case 2: Valid "enabled" property with "true" value (after initialization)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::ENABLE_SNAPSHOT)] = "true";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Test case 3: Valid "enabled" property with "false" value
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::ENABLE_SNAPSHOT)] = "false";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Test case 4: Invalid "enabled" property value
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::ENABLE_SNAPSHOT)] = "invalid";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// Test case 5: Valid "max_reserved_snapshots" property (minimum value)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::MAX_RESERVED_SNAPSHOTS)] = "0";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Test case 6: Valid "max_reserved_snapshots" property (maximum value)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::MAX_RESERVED_SNAPSHOTS)] = "35";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Test case 7: Valid "max_reserved_snapshots" property (middle value)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::MAX_RESERVED_SNAPSHOTS)] = "10";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Test case 8: Invalid "max_reserved_snapshots" property (negative value)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::MAX_RESERVED_SNAPSHOTS)] = "-1";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// Test case 9: Invalid "max_reserved_snapshots" property (too large value)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::MAX_RESERVED_SNAPSHOTS)] = "36";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// Test case 10: Invalid "max_reserved_snapshots" property (non-numeric value)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::MAX_RESERVED_SNAPSHOTS)] = "abc";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// Test case 11: Valid "snapshot_interval_seconds" property (minimum value)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::SNAPSHOT_INTERVAL_SECONDS)] = "3600";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Test case 12: Valid "snapshot_interval_seconds" property (large value)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::SNAPSHOT_INTERVAL_SECONDS)] = "14400";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Test case 13: Invalid "snapshot_interval_seconds" property (too small value)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::SNAPSHOT_INTERVAL_SECONDS)] = "3599";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// Test case 14: Invalid "snapshot_interval_seconds" property (non-numeric value)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::SNAPSHOT_INTERVAL_SECONDS)] = "invalid";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// Test case 15: Unsupported property key
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())["unsupported_property"] = "value";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// Test case 16: Empty properties map
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
// Don't add any properties - properties map will be empty
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
}
// Test case 17: Multiple valid properties in single request
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::ENABLE_SNAPSHOT)] = "true";
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::MAX_RESERVED_SNAPSHOTS)] = "20";
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::SNAPSHOT_INTERVAL_SECONDS)] = "12000";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Test case 18: Mixed valid and invalid properties (should fail on first invalid)
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::ENABLE_SNAPSHOT)] = "true";
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::MAX_RESERVED_SNAPSHOTS)] = "10";
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::SNAPSHOT_INTERVAL_SECONDS)] = "12000";
(*req.mutable_properties())["unsupported_property"] = "value";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT) << res.ShortDebugString();
}
// Test case 19: Non-existent instance ID
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("non_existent_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::ENABLE_SNAPSHOT)] = "true";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::CLUSTER_NOT_FOUND);
}
}
TEST(MetaServiceTest, SnapshotConfigLimitsTest) {
auto meta_service = get_meta_service();
// Create a test instance first
{
brpc::Controller cntl;
CreateInstanceRequest req;
req.set_instance_id("test_snapshot_config_instance");
req.set_user_id("test_user");
req.set_name("test_name");
auto* sp = SyncPoint::get_instance();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->enable_processing();
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
auto* sp = SyncPoint::get_instance();
sp->set_call_back("notify_refresh_instance_return",
[](auto&& args) { *try_any_cast<bool*>(args.back()) = true; });
sp->enable_processing();
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
SyncPoint::get_instance()->disable_processing();
};
// Initialize snapshot switch status to OFF so we can test snapshot functionality
{
InstanceKeyInfo key_info {"test_snapshot_config_instance"};
std::string key;
std::string val;
instance_key(key_info, &key);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
InstanceInfoPB instance;
instance.ParseFromString(val);
instance.set_snapshot_switch_status(SNAPSHOT_SWITCH_OFF);
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
}
// Enable snapshot for this instance
{
brpc::Controller cntl;
AlterInstanceRequest alter_req;
AlterInstanceResponse alter_res;
alter_req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
alter_req.set_instance_id("test_snapshot_config_instance");
(*alter_req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::ENABLE_SNAPSHOT)] = "true";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&alter_req, &alter_res, nullptr);
ASSERT_EQ(alter_res.status().code(), MetaServiceCode::OK);
}
// Save original config values
int32_t orig_min_interval = config::snapshot_min_interval_seconds;
int32_t orig_max_reserved = config::snapshot_max_reserved_num;
// Temporarily modify config for testing
config::snapshot_min_interval_seconds = 1800; // 30 minutes
config::snapshot_max_reserved_num = 20;
// Test case 1: interval below minimum limit
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_config_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::SNAPSHOT_INTERVAL_SECONDS)] = "1799"; // Below min limit
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_TRUE(res.status().msg().find("minimum is 1800") != std::string::npos);
}
// Test case 2: interval within limits should succeed
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_config_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::SNAPSHOT_INTERVAL_SECONDS)] = "3600"; // Within limits
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Test case 3: max_reserved_snapshots above maximum limit
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_config_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::MAX_RESERVED_SNAPSHOTS)] = "21"; // Above max limit
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_TRUE(res.status().msg().find("maximum is 20") != std::string::npos);
}
// Test case 4: max_reserved_snapshots within limits should succeed
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_config_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::MAX_RESERVED_SNAPSHOTS)] = "10"; // Within limits
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Test case 5: unknown property should fail
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_snapshot_config_instance");
(*req.mutable_properties())["unknown_property"] = "value";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
ASSERT_TRUE(res.status().msg().find("unknown snapshot property: unknown_property") !=
std::string::npos);
}
// Restore original config values
config::snapshot_min_interval_seconds = orig_min_interval;
config::snapshot_max_reserved_num = orig_max_reserved;
}
TEST(MetaServiceTest, SnapshotDefaultValuesTest) {
auto meta_service = get_meta_service();
// Create a test instance first
{
brpc::Controller cntl;
CreateInstanceRequest req;
req.set_instance_id("test_instance");
req.set_user_id("test_user");
req.set_name("test_name");
auto* sp = SyncPoint::get_instance();
sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
auto* ret = try_any_cast<int*>(args[0]);
*ret = 0;
auto* key = try_any_cast<std::string*>(args[1]);
*key = "test";
auto* key_id = try_any_cast<int64_t*>(args[2]);
*key_id = 1;
});
sp->set_call_back("get_instance_id", [&](auto&& args) {
auto* ret = try_any_cast_ret<std::string>(args);
ret->first = "test_instance";
ret->second = true;
});
sp->enable_processing();
CreateInstanceResponse res;
meta_service->create_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
sp->clear_all_call_backs();
sp->clear_trace();
sp->disable_processing();
}
// Initialize snapshot switch status to OFF so we can test snapshot functionality
{
InstanceKeyInfo key_info {"test_instance"};
std::string key;
std::string val;
instance_key(key_info, &key);
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
InstanceInfoPB instance;
instance.ParseFromString(val);
instance.set_snapshot_switch_status(SNAPSHOT_SWITCH_OFF);
val = instance.SerializeAsString();
txn->put(key, val);
ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
}
// First enable snapshot - should set default values
{
brpc::Controller cntl;
AlterInstanceRequest req;
AlterInstanceResponse res;
req.set_op(AlterInstanceRequest::SET_SNAPSHOT_PROPERTY);
req.set_instance_id("test_instance");
(*req.mutable_properties())[AlterInstanceRequest_SnapshotProperty_Name(
AlterInstanceRequest::ENABLE_SNAPSHOT)] = "true";
meta_service->alter_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
// Verify default values were set by checking instance
{
brpc::Controller cntl;
GetInstanceRequest req;
GetInstanceResponse res;
req.set_instance_id("test_instance");
req.set_cloud_unique_id("test_snapshot_defaults_unique_id");
meta_service->get_instance(reinterpret_cast<::google::protobuf::RpcController*>(&cntl),
&req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
const auto& instance = res.instance();
ASSERT_EQ(instance.snapshot_switch_status(), SnapshotSwitchStatus::SNAPSHOT_SWITCH_ON);
ASSERT_EQ(instance.snapshot_interval_seconds(), config::snapshot_min_interval_seconds);
ASSERT_EQ(instance.max_reserved_snapshot(), 1);
}
}
TEST(MetaServiceTest, CreateTabletIdempotentAndHandlingError) {
DORIS_CLOUD_DEFER {
SyncPoint::get_instance()->clear_all_call_backs();
SyncPoint::get_instance()->disable_processing();
};
size_t case_num = 0;
auto* sp = SyncPoint::get_instance();
sp->set_call_back("meta_service_test:get_meta_tablet_key_error", [&case_num](auto&& args) {
if (++case_num == 3) {
auto* code = try_any_cast<TxnErrorCode*>(args[0]);
*code = TxnErrorCode::TXN_INVALID_DATA;
}
});
sp->enable_processing();
auto meta_service = get_meta_service();
brpc::Controller cntl;
CreateTabletsRequest req;
CreateTabletsResponse res;
int64_t table_id = 100;
int64_t index_id = 200;
int64_t partition_id = 300;
int64_t tablet_id = 400;
req.set_db_id(1); // default db_id
add_tablet(req, table_id, index_id, partition_id, tablet_id);
// normal create
meta_service->create_tablets(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
// idempotent
meta_service->create_tablets(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
// error handling
meta_service->create_tablets(&cntl, &req, &res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::KV_TXN_GET_ERR);
}
TEST(MetaServiceTest, RowsetVisibleTimeTest) {
auto meta_service = get_meta_service();
using namespace std::chrono;
int64_t txn_id = -1;
// begin txn
{
brpc::Controller cntl;
BeginTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
TxnInfoPB txn_info_pb;
txn_info_pb.set_db_id(666);
txn_info_pb.set_label("test_label");
txn_info_pb.add_table_ids(1234);
txn_info_pb.set_timeout_ms(36000);
req.mutable_txn_info()->CopyFrom(txn_info_pb);
BeginTxnResponse res;
meta_service->begin_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
txn_id = res.txn_id();
}
// mock rowset and tablet
int64_t tablet_id_base = 1103;
for (int i = 0; i < 5; ++i) {
create_tablet(meta_service.get(), 1234, 1235, 1236, tablet_id_base + i);
auto tmp_rowset = create_rowset(txn_id, tablet_id_base + i);
CreateRowsetResponse res;
commit_rowset(meta_service.get(), tmp_rowset, res);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
{
brpc::Controller cntl;
CommitTxnRequest req;
req.set_cloud_unique_id("test_cloud_unique_id");
req.set_db_id(666);
req.set_txn_id(txn_id);
CommitTxnResponse res;
meta_service->commit_txn(reinterpret_cast<::google::protobuf::RpcController*>(&cntl), &req,
&res, nullptr);
ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
}
for (int i = 0; i < 5; ++i) {
int64_t tablet_id = tablet_id_base + i;
int64_t ver = 2;
std::string rowset_key = meta_rowset_key({mock_instance, tablet_id, ver});
std::string val;
std::unique_ptr<Transaction> txn;
ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
ASSERT_EQ(txn->get(rowset_key, &val), TxnErrorCode::TXN_OK);
RowsetMetaCloudPB rowset_pb;
ASSERT_TRUE(rowset_pb.ParseFromString(val));
ASSERT_TRUE(rowset_pb.has_visible_ts_ms());
std::cout << rowset_pb.visible_ts_ms() << "\n";
ASSERT_GT(rowset_pb.visible_ts_ms(), 0);
auto visible_tp = time_point<system_clock>(milliseconds(rowset_pb.visible_ts_ms()));
std::time_t visible_time = system_clock::to_time_t(visible_tp);
std::cout << "visible time: "
<< std::put_time(std::localtime(&visible_time), "%Y%m%d %H:%M:%S") << "\n";
}
}
} // namespace doris::cloud