From d4f86847a97433154a99da6e04172f81cb3fa5e1 Mon Sep 17 00:00:00 2001
From: hotwords <31656766+hotwords123@users.noreply.github.com>
Date: Wed, 12 Jun 2024 11:23:43 +0800
Subject: [PATCH] fix(mvcc): fix commit xid may be accidentally reused (#26)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

**问题:** 在事务恢复阶段,`MTR_BEGIN` 日志对应的 trx id 会在 `MvccTrxManager` 类的
`create_trx(int32_t trx_id)` 方法中更新 `current_trx_id` 以避免重复使用,符合预期。但是
`MTR_COMMIT` 日志所使用的 commit xid 没有更新,可能会被重用而导致错误的结果。

**解决方案:** 增加 `update_trx_id` 方法用于确保 `current_trx_id` 不小于给定的参数。在 recover
阶段,`MvccTrx` 提交时应当调用该方法更新 `current_trx_id`。
---
 .../include/storage_engine/transaction/mvcc_trx.h     |  3 +++
 src/server/storage_engine/transaction/mvcc_trx.cpp    | 11 +++++++++++
 2 files changed, 14 insertions(+)

diff --git a/src/server/include/storage_engine/transaction/mvcc_trx.h b/src/server/include/storage_engine/transaction/mvcc_trx.h
index 03eb511..93cd24c 100644
--- a/src/server/include/storage_engine/transaction/mvcc_trx.h
+++ b/src/server/include/storage_engine/transaction/mvcc_trx.h
@@ -23,6 +23,9 @@ class MvccTrxManager : public TrxManager
   int32_t next_trx_id();
   int32_t max_trx_id() const;
 
+  // 在 recover 场景下使用,确保当前事务 id 不小于 trx_id
+  void update_trx_id(int32_t trx_id);
+
 private:
   std::vector<FieldMeta> fields_; // 存储事务数据需要用到的字段元数据,所有表结构都需要带
   std::atomic<int32_t> current_trx_id_{0};
diff --git a/src/server/storage_engine/transaction/mvcc_trx.cpp b/src/server/storage_engine/transaction/mvcc_trx.cpp
index aaef737..7570eab 100644
--- a/src/server/storage_engine/transaction/mvcc_trx.cpp
+++ b/src/server/storage_engine/transaction/mvcc_trx.cpp
@@ -95,6 +95,12 @@ int32_t MvccTrxManager::max_trx_id() const
   return numeric_limits<int32_t>::max();
 }
 
+void MvccTrxManager::update_trx_id(int32_t trx_id)
+{
+  int32_t old_trx_id = current_trx_id_;
+  while (old_trx_id < trx_id && !current_trx_id_.compare_exchange_weak(old_trx_id, trx_id));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 MvccTrx::MvccTrx(MvccTrxManager &kit, LogManager *log_manager) : trx_kit_(kit), log_manager_(log_manager)
 {}
@@ -168,6 +174,11 @@ RC MvccTrx::commit_with_trx_id(int32_t commit_xid)
   RC rc = RC::SUCCESS;
   started_ = false;
 
+  if (recovering_) {
+    // 在事务恢复时,更新当前事务 id 避免被后续事务重用
+    trx_kit_.update_trx_id(commit_xid);
+  }
+
   for (const Operation &operation : operations_) {
     switch (operation.type()) {
       case Operation::Type::INSERT: {