This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch branch-incremental-computation
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-incremental-computation
by this push:
new f62b9cc9a6d branch-incremental-computation: [fix](binlog) Fix row
binlog recovery for multi-tablet transactions #67508 (#67712)
f62b9cc9a6d is described below
commit f62b9cc9a6d9148f0a721deec819a41381a5c3ca
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Wed Sep 9 15:11:59 2026 +0800
branch-incremental-computation: [fix](binlog) Fix row binlog recovery for
multi-tablet transactions #67508 (#67712)
Cherry-picked from #67508
---
be/src/storage/data_dir.cpp | 11 +-
be/src/storage/tablet/tablet_manager.cpp | 25 ++++-
be/src/storage/tablet/tablet_meta.cpp | 6 +
be/src/storage/tablet/tablet_meta.h | 3 +
be/test/olap/rowset/group_rowset_builder_test.cpp | 131 ++++++++++++++++++++--
5 files changed, 161 insertions(+), 15 deletions(-)
diff --git a/be/src/storage/data_dir.cpp b/be/src/storage/data_dir.cpp
index 6b580b48618..6419351700a 100644
--- a/be/src/storage/data_dir.cpp
+++ b/be/src/storage/data_dir.cpp
@@ -520,11 +520,11 @@ Status DataDir::load() {
}
// Row binlog rowset is now a normal rowset under its own binlog tablet,
loaded above.
- // Index them by txn id so each base rowset can re-attach its paired
binlog rowset on recovery.
- std::map<int64_t, RowsetMetaSharedPtr> txn_id_to_row_binlog_meta;
+ // Index them by txn and tablet id so each base rowset can re-attach its
paired binlog rowset.
+ std::map<std::pair<int64_t, int64_t>, RowsetMetaSharedPtr>
row_binlog_metas;
for (auto&& rowset_meta : dir_rowset_metas) {
if (rowset_meta->is_row_binlog()) {
- txn_id_to_row_binlog_meta[rowset_meta->txn_id()] = rowset_meta;
+ row_binlog_metas[{rowset_meta->txn_id(),
rowset_meta->tablet_id()}] = rowset_meta;
}
}
@@ -558,8 +558,9 @@ Status DataDir::load() {
}
RowBinlogTxnInfo attach_row_binlog;
- if (auto it = txn_id_to_row_binlog_meta.find(rowset_meta->txn_id());
- it != txn_id_to_row_binlog_meta.end()) {
+ if (auto it = row_binlog_metas.find(
+ {rowset_meta->txn_id(),
tablet->tablet_meta()->binlog_tablet_id()});
+ it != row_binlog_metas.end()) {
const RowsetMetaSharedPtr& attach_row_binlog_rowset_meta =
it->second;
DCHECK_EQ(attach_row_binlog_rowset_meta->rowset_state(),
rowset_meta->rowset_state());
TabletSharedPtr binlog_tablet =
_engine.tablet_manager()->get_tablet(
diff --git a/be/src/storage/tablet/tablet_manager.cpp
b/be/src/storage/tablet/tablet_manager.cpp
index dea24b37de6..5db2159c1e4 100644
--- a/be/src/storage/tablet/tablet_manager.cpp
+++ b/be/src/storage/tablet/tablet_manager.cpp
@@ -288,9 +288,11 @@ Status TabletManager::create_tablet(const
TCreateTabletReq& request, std::vector
// same) already exist, then just return true(an duplicate request). But if
// tablet_id exist but with different schema_hash, return an error(report
task will
// eventually trigger its deletion).
+ bool tablet_exists = false;
{
SCOPED_TIMER(ADD_TIMER(profile, "GetTabletUnlocked"));
- if (_get_tablet_unlocked(tablet_id) != nullptr) {
+ tablet_exists = _get_tablet_unlocked(tablet_id) != nullptr;
+ if (tablet_exists && !is_colocated_row_binlog) {
LOG(INFO) << "success to create tablet. tablet already exist.
tablet_id=" << tablet_id;
return Status::OK();
}
@@ -331,6 +333,24 @@ Status TabletManager::create_tablet(const
TCreateTabletReq& request, std::vector
}
}
+ auto persist_row_binlog_pair = [&]() {
+ CHECK(is_colocated_row_binlog);
+ std::lock_guard base_tablet_wlock(base_tablet->get_header_lock());
+ CHECK(base_tablet->tablet_meta()->binlog_tablet_id() == 0 ||
+ base_tablet->tablet_meta()->binlog_tablet_id() == tablet_id)
+ << "base tablet " << base_tablet->tablet_id()
+ << " is already paired with row-binlog tablet "
+ << base_tablet->tablet_meta()->binlog_tablet_id() << ", new
row-binlog tablet "
+ << tablet_id;
+ base_tablet->tablet_meta()->set_binlog_tablet_id(tablet_id);
+ base_tablet->save_meta();
+ };
+ if (tablet_exists) {
+ persist_row_binlog_pair();
+ LOG(INFO) << "success to create tablet. tablet already exist.
tablet_id=" << tablet_id;
+ return Status::OK();
+ }
+
TabletSharedPtr tablet = _internal_create_tablet_unlocked(
request, is_schema_change_or_atomic_restore,
is_colocated_row_binlog, base_tablet.get(),
stores, profile);
@@ -339,6 +359,9 @@ Status TabletManager::create_tablet(const TCreateTabletReq&
request, std::vector
return Status::Error<CE_CMD_PARAMS_ERROR>("fail to create tablet.
tablet_id={}",
request.tablet_id);
}
+ if (is_colocated_row_binlog) {
+ persist_row_binlog_pair();
+ }
LOG(INFO) << "success to create tablet. tablet_id=" << tablet_id
<< ", tablet_path=" << tablet->tablet_path();
diff --git a/be/src/storage/tablet/tablet_meta.cpp
b/be/src/storage/tablet/tablet_meta.cpp
index 1f6ee1012da..7b1908f20c3 100644
--- a/be/src/storage/tablet/tablet_meta.cpp
+++ b/be/src/storage/tablet/tablet_meta.cpp
@@ -269,6 +269,7 @@ TabletMeta::TabletMeta(const TabletMeta& b)
_delete_bitmap(b._delete_bitmap),
_binlog_config(b._binlog_config),
_tablet_role(b._tablet_role),
+ _binlog_tablet_id(b._binlog_tablet_id),
_compaction_policy(b._compaction_policy),
_time_series_compaction_goal_size_mbytes(b._time_series_compaction_goal_size_mbytes),
_time_series_compaction_file_count_threshold(
@@ -902,6 +903,7 @@ void TabletMeta::init_from_pb(const TabletMetaPB&
tablet_meta_pb) {
_binlog_config = tablet_meta_pb.binlog_config();
}
_tablet_role = tablet_meta_pb.tablet_role();
+ _binlog_tablet_id = tablet_meta_pb.binlog_tablet_id();
_compaction_policy = tablet_meta_pb.compaction_policy();
_time_series_compaction_goal_size_mbytes =
tablet_meta_pb.time_series_compaction_goal_size_mbytes();
@@ -1005,6 +1007,9 @@ void TabletMeta::to_meta_pb(TabletMetaPB* tablet_meta_pb,
bool cloud_get_rowset_
}
_binlog_config.to_pb(tablet_meta_pb->mutable_binlog_config());
tablet_meta_pb->set_tablet_role(_tablet_role);
+ if (_binlog_tablet_id > 0) {
+ tablet_meta_pb->set_binlog_tablet_id(_binlog_tablet_id);
+ }
tablet_meta_pb->set_compaction_policy(compaction_policy());
tablet_meta_pb->set_time_series_compaction_goal_size_mbytes(
time_series_compaction_goal_size_mbytes());
@@ -1249,6 +1254,7 @@ bool operator==(const TabletMeta& a, const TabletMeta& b)
{
if (a._in_restore_mode != b._in_restore_mode) return false;
if (a._preferred_rowset_type != b._preferred_rowset_type) return false;
if (a._storage_policy_id != b._storage_policy_id) return false;
+ if (a._binlog_tablet_id != b._binlog_tablet_id) return false;
if (a._compaction_policy != b._compaction_policy) return false;
if (a._time_series_compaction_goal_size_mbytes !=
b._time_series_compaction_goal_size_mbytes)
return false;
diff --git a/be/src/storage/tablet/tablet_meta.h
b/be/src/storage/tablet/tablet_meta.h
index 0efce3d3f2e..3b01f94df8a 100644
--- a/be/src/storage/tablet/tablet_meta.h
+++ b/be/src/storage/tablet/tablet_meta.h
@@ -280,6 +280,8 @@ public:
return _tablet_role == TabletRolePB::TABLET_ROLE_ROW_BINLOG;
}
void set_tablet_role(TabletRolePB tablet_role) { _tablet_role =
tablet_role; }
+ int64_t binlog_tablet_id() const { return _binlog_tablet_id; }
+ void set_binlog_tablet_id(int64_t binlog_tablet_id) { _binlog_tablet_id =
binlog_tablet_id; }
void set_compaction_policy(std::string compaction_policy) {
_compaction_policy = compaction_policy;
@@ -395,6 +397,7 @@ private:
// binlog config
BinlogConfig _binlog_config {};
TabletRolePB _tablet_role = TabletRolePB::TABLET_ROLE_DATA;
+ int64_t _binlog_tablet_id = 0;
// meta for compaction
std::string _compaction_policy;
diff --git a/be/test/olap/rowset/group_rowset_builder_test.cpp
b/be/test/olap/rowset/group_rowset_builder_test.cpp
index 325604f712a..a4b65e95d6a 100644
--- a/be/test/olap/rowset/group_rowset_builder_test.cpp
+++ b/be/test/olap/rowset/group_rowset_builder_test.cpp
@@ -22,6 +22,8 @@
#include <stdlib.h>
#include <unistd.h>
+#include <array>
+#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -39,6 +41,7 @@
#include "storage/storage_engine.h"
#include "storage/tablet/tablet.h"
#include "storage/tablet/tablet_manager.h"
+#include "storage/tablet/tablet_meta_manager.h"
#include "storage/tablet_info.h"
#include "testutil/creators.h"
@@ -47,14 +50,7 @@ namespace doris {
static const uint32_t MAX_PATH_LEN = 1024;
static StorageEngine* engine_ref = nullptr;
-static void set_up() {
- char buffer[MAX_PATH_LEN];
- EXPECT_NE(getcwd(buffer, MAX_PATH_LEN), nullptr);
- config::storage_root_path = std::string(buffer) + "/data_test";
- auto st =
io::global_local_filesystem()->delete_directory(config::storage_root_path);
- ASSERT_TRUE(st.ok()) << st;
- st =
io::global_local_filesystem()->create_directory(config::storage_root_path);
- ASSERT_TRUE(st.ok()) << st;
+static void open_engine() {
std::vector<StorePath> paths;
paths.emplace_back(config::storage_root_path, -1);
@@ -64,10 +60,26 @@ static void set_up() {
engine_ref = engine.get();
Status s = engine->open();
ASSERT_TRUE(s.ok()) << s;
+ ExecEnv::GetInstance()->set_storage_engine(std::move(engine));
+}
+static void set_up() {
+ char buffer[MAX_PATH_LEN];
+ EXPECT_NE(getcwd(buffer, MAX_PATH_LEN), nullptr);
+ config::storage_root_path = std::string(buffer) + "/data_test";
+ auto st =
io::global_local_filesystem()->delete_directory(config::storage_root_path);
+ ASSERT_TRUE(st.ok()) << st;
+ st =
io::global_local_filesystem()->create_directory(config::storage_root_path);
+ ASSERT_TRUE(st.ok()) << st;
ExecEnv* exec_env = doris::ExecEnv::GetInstance();
exec_env->set_memtable_memory_limiter(new MemTableMemoryLimiter());
- exec_env->set_storage_engine(std::move(engine));
+ open_engine();
+}
+
+static void restart_engine() {
+ engine_ref = nullptr;
+ ExecEnv::GetInstance()->set_storage_engine(nullptr);
+ open_engine();
}
static void tear_down() {
@@ -167,4 +179,105 @@ TEST_F(GroupRowsetBuilderTest, buildWithRowBinlogMeta) {
ASSERT_TRUE(res.ok());
}
+TEST_F(GroupRowsetBuilderTest, recoverMultipleRowBinlogPairsInOneTxn) {
+ constexpr int64_t partition_id = 10100;
+ constexpr int64_t txn_id = 20100;
+ constexpr int64_t index_id = 30100;
+ constexpr int64_t row_binlog_index_id = 30101;
+ constexpr int32_t schema_hash = 40100;
+ constexpr int32_t row_binlog_schema_hash = 40101;
+ constexpr std::array<std::pair<int64_t, int64_t>, 2> tablet_pairs =
{std::pair {10100, 10101},
+
std::pair {10200, 10201}};
+
+ auto base_request = testutil::create_tablet_request(
+ 0, schema_hash, partition_id, 1, TKeysType::UNIQUE_KEYS,
+ {{"k1", TPrimitiveType::INT, true}, {"v1", TPrimitiveType::INT,
false}});
+ base_request.__set_enable_unique_key_merge_on_write(true);
+ testutil::enable_row_binlog(&base_request);
+ auto row_binlog_schema =
testutil::create_row_binlog_tablet_schema(base_request.tablet_schema,
+
row_binlog_schema_hash);
+
+ RuntimeProfile profile("CreateTablet");
+ for (const auto& [base_tablet_id, row_binlog_tablet_id] : tablet_pairs) {
+ base_request.tablet_id = base_tablet_id;
+ ASSERT_TRUE(engine_ref->create_tablet(base_request, &profile).ok());
+
+ auto row_binlog_request = base_request;
+ row_binlog_request.tablet_id = row_binlog_tablet_id;
+ row_binlog_request.tablet_schema = row_binlog_schema;
+ row_binlog_request.__set_base_tablet_id(base_tablet_id);
+
row_binlog_request.__set_tablet_role(TTabletRole::TABLET_ROLE_ROW_BINLOG);
+ ASSERT_TRUE(engine_ref->create_tablet(row_binlog_request,
&profile).ok());
+
+ auto base_tablet =
engine_ref->tablet_manager()->get_tablet(base_tablet_id);
+ ASSERT_NE(base_tablet, nullptr);
+ TabletMetaPB in_memory_meta_pb;
+ base_tablet->tablet_meta()->to_meta_pb(&in_memory_meta_pb, false);
+ EXPECT_EQ(in_memory_meta_pb.binlog_tablet_id(), row_binlog_tablet_id);
+
+ TabletMetaSharedPtr persisted_meta = std::make_shared<TabletMeta>();
+ ASSERT_TRUE(TabletMetaManager::get_meta(base_tablet->data_dir(),
base_tablet_id,
+ schema_hash, persisted_meta)
+ .ok());
+ TabletMetaPB persisted_meta_pb;
+ persisted_meta->to_meta_pb(&persisted_meta_pb, false);
+ EXPECT_EQ(persisted_meta_pb.binlog_tablet_id(), row_binlog_tablet_id);
+ }
+
+ TDescriptorTable tdesc_tbl =
+ testutil::create_descriptor_table({{TYPE_INT, "k1", false},
{TYPE_INT, "v1", false}});
+ auto schema_param = testutil::create_table_schema_param(
+ tdesc_tbl, index_id, schema_hash,
base_request.tablet_schema.columns,
+ row_binlog_index_id, row_binlog_schema_hash,
&row_binlog_schema.columns);
+ ASSERT_NE(schema_param, nullptr);
+
+ PUniqueId load_id;
+ load_id.set_hi(0);
+ load_id.set_lo(1);
+ for (const auto& [base_tablet_id, row_binlog_tablet_id] : tablet_pairs) {
+ WriteRequest data_req;
+ data_req.tablet_id = base_tablet_id;
+ data_req.schema_hash = schema_hash;
+ data_req.txn_id = txn_id;
+ data_req.partition_id = partition_id;
+ data_req.index_id = index_id;
+ data_req.load_id = load_id;
+ data_req.table_schema_param = schema_param;
+ data_req.write_req_type = WriteRequestType::DATA;
+
+ WriteRequest row_binlog_req = data_req;
+ row_binlog_req.tablet_id = row_binlog_tablet_id;
+ row_binlog_req.index_id = row_binlog_index_id;
+ row_binlog_req.schema_hash = row_binlog_schema_hash;
+ row_binlog_req.write_req_type = WriteRequestType::ROW_BINLOG;
+
+ WriteRequest group_req = data_req;
+ group_req.write_req_type = WriteRequestType::GROUP;
+
+ GroupRowsetBuilder builder(*engine_ref, group_req, data_req,
row_binlog_req, &profile);
+ ASSERT_TRUE(builder.init().ok());
+ ASSERT_TRUE(builder.rowset_writer()->flush().ok());
+ ASSERT_TRUE(builder.build_rowset().ok());
+ ASSERT_TRUE(builder.commit_txn().ok());
+ }
+
+ restart_engine();
+
+ std::map<TabletInfo, RowsetSharedPtr> rowsets;
+ std::map<TabletInfo, std::shared_ptr<TabletTxnInfo>> txn_infos;
+ engine_ref->txn_manager()->get_txn_related_tablets(txn_id, partition_id,
&rowsets, &txn_infos);
+ ASSERT_EQ(txn_infos.size(), tablet_pairs.size());
+ for (const auto& [base_tablet_id, row_binlog_tablet_id] : tablet_pairs) {
+ auto base_tablet =
engine_ref->tablet_manager()->get_tablet(base_tablet_id);
+ ASSERT_NE(base_tablet, nullptr);
+ auto txn_info = txn_infos.find(base_tablet->get_tablet_info());
+ ASSERT_NE(txn_info, txn_infos.end());
+ ASSERT_NE(txn_info->second->attach_row_binlog.tablet, nullptr);
+ ASSERT_NE(txn_info->second->attach_row_binlog.rowset, nullptr);
+ EXPECT_EQ(txn_info->second->attach_row_binlog.tablet->tablet_id(),
row_binlog_tablet_id);
+
EXPECT_EQ(txn_info->second->attach_row_binlog.rowset->rowset_meta()->tablet_id(),
+ row_binlog_tablet_id);
+ }
+}
+
} // namespace doris
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]