Coverage Report

Created: 2026-08-14 11:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/txn/txn_manager.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "storage/txn/txn_manager.h"
19
20
#include <bvar/bvar.h>
21
#include <fmt/format.h>
22
#include <fmt/ranges.h>
23
#include <thrift/protocol/TDebugProtocol.h>
24
#include <time.h>
25
26
#include <filesystem>
27
#include <iterator>
28
#include <list>
29
#include <new>
30
#include <ostream>
31
#include <queue>
32
#include <set>
33
#include <string>
34
#include <unordered_set>
35
36
#include "common/config.h"
37
#include "common/logging.h"
38
#include "common/status.h"
39
#include "load/delta_writer/delta_writer.h"
40
#include "storage/binlog.h"
41
#include "storage/data_dir.h"
42
#include "storage/olap_common.h"
43
#include "storage/partial_update_info.h"
44
#include "storage/rowset/beta_rowset.h"
45
#include "storage/rowset/pending_rowset_helper.h"
46
#include "storage/rowset/rowset_meta.h"
47
#include "storage/rowset/rowset_meta_manager.h"
48
#include "storage/schema_change/schema_change.h"
49
#include "storage/segment/segment_loader.h"
50
#include "storage/storage_engine.h"
51
#include "storage/tablet/tablet_manager.h"
52
#include "storage/tablet/tablet_meta.h"
53
#include "storage/tablet/tablet_meta_manager.h"
54
#include "storage/task/engine_publish_version_task.h"
55
#include "util/debug_points.h"
56
#include "util/time.h"
57
58
namespace doris {
59
class OlapMeta;
60
} // namespace doris
61
62
using std::map;
63
using std::pair;
64
using std::set;
65
using std::string;
66
using std::stringstream;
67
using std::vector;
68
69
namespace doris {
70
using namespace ErrorCode;
71
72
bvar::Adder<int64_t> g_tablet_txn_info_txn_partitions_count("tablet_txn_info_txn_partitions_count");
73
74
TxnManager::TxnManager(StorageEngine& engine, int32_t txn_map_shard_size, int32_t txn_shard_size)
75
615
        : _engine(engine),
76
615
          _txn_map_shard_size(txn_map_shard_size),
77
615
          _txn_shard_size(txn_shard_size) {
78
615
    DCHECK_GT(_txn_map_shard_size, 0);
79
615
    DCHECK_GT(_txn_shard_size, 0);
80
615
    DCHECK_EQ(_txn_map_shard_size & (_txn_map_shard_size - 1), 0);
81
615
    DCHECK_EQ(_txn_shard_size & (_txn_shard_size - 1), 0);
82
615
    _txn_map_locks = new std::shared_mutex[_txn_map_shard_size];
83
615
    _txn_tablet_maps = new txn_tablet_map_t[_txn_map_shard_size];
84
615
    _txn_partition_maps = new txn_partition_map_t[_txn_map_shard_size];
85
615
    _txn_mutex = new std::shared_mutex[_txn_shard_size];
86
    // For debugging
87
615
    _tablet_version_cache = std::make_unique<TabletVersionCache>(100000);
88
615
}
89
90
// prepare txn should always be allowed because ingest task will be retried
91
// could not distinguish rollup, schema change or base table, prepare txn successfully will allow
92
// ingest retried
93
Status TxnManager::prepare_txn(TPartitionId partition_id, const Tablet& tablet,
94
                               TTransactionId transaction_id, const PUniqueId& load_id,
95
47.5k
                               bool ingest) {
96
    // check if the tablet has already been shutdown. If it has, it indicates that
97
    // it is an old tablet, and data should not be imported into the old tablet.
98
    // Otherwise, it may lead to data loss during migration.
99
47.5k
    if (tablet.tablet_state() == TABLET_SHUTDOWN) {
100
1
        return Status::InternalError<false>(
101
1
                "The tablet's state is shutdown, tablet_id: {}. The tablet may have been dropped "
102
1
                "or migrationed. Please check if the table has been dropped or try again.",
103
1
                tablet.tablet_id());
104
1
    }
105
47.5k
    return prepare_txn(partition_id, transaction_id, tablet.tablet_id(), tablet.tablet_uid(),
106
47.5k
                       load_id, ingest);
107
47.5k
}
108
109
// most used for ut
110
Status TxnManager::prepare_txn(TPartitionId partition_id, TTransactionId transaction_id,
111
                               TTabletId tablet_id, TabletUid tablet_uid, const PUniqueId& load_id,
112
47.5k
                               bool ingest) {
113
47.5k
    TxnKey key(partition_id, transaction_id);
114
47.5k
    TabletInfo tablet_info(tablet_id, tablet_uid);
115
47.5k
    std::lock_guard<std::shared_mutex> txn_wrlock(_get_txn_map_lock(transaction_id));
116
47.5k
    txn_tablet_map_t& txn_tablet_map = _get_txn_tablet_map(transaction_id);
117
118
47.5k
    DBUG_EXECUTE_IF("TxnManager.prepare_txn.random_failed", {
119
47.5k
        if (rand() % 100 < (100 * dp->param("percent", 0.5))) {
120
47.5k
            LOG_WARNING("TxnManager.prepare_txn.random_failed random failed")
121
47.5k
                    .tag("txn_id", transaction_id)
122
47.5k
                    .tag("tablet_id", tablet_id);
123
47.5k
            return Status::InternalError("debug prepare txn random failed");
124
47.5k
        }
125
47.5k
    });
126
47.5k
    DBUG_EXECUTE_IF("TxnManager.prepare_txn.wait", {
127
47.5k
        if (auto wait = dp->param<int>("duration", 0); wait > 0) {
128
47.5k
            LOG_WARNING("TxnManager.prepare_txn.wait")
129
47.5k
                    .tag("txn_id", transaction_id)
130
47.5k
                    .tag("tablet_id", tablet_id)
131
47.5k
                    .tag("wait ms", wait);
132
47.5k
            std::this_thread::sleep_for(std::chrono::milliseconds(wait));
133
47.5k
        }
134
47.5k
    });
135
136
    /// Step 1: check if the transaction is already exist
137
47.5k
    do {
138
47.5k
        auto iter = txn_tablet_map.find(key);
139
47.5k
        if (iter == txn_tablet_map.end()) {
140
6.03k
            break;
141
6.03k
        }
142
143
        // exist TxnKey
144
41.4k
        auto& txn_tablet_info_map = iter->second;
145
41.4k
        auto load_itr = txn_tablet_info_map.find(tablet_info);
146
41.4k
        if (load_itr == txn_tablet_info_map.end()) {
147
41.4k
            break;
148
41.4k
        }
149
150
        // found load for txn,tablet
151
2
        auto& load_info = load_itr->second;
152
        // case 1: user commit rowset, then the load id must be equal
153
        // check if load id is equal
154
2
        if (load_info->load_id.hi() == load_id.hi() && load_info->load_id.lo() == load_id.lo() &&
155
2
            load_info->rowset != nullptr) {
156
1
            LOG(WARNING) << "find transaction exists when add to engine."
157
1
                         << "partition_id: " << key.first << ", transaction_id: " << key.second
158
1
                         << ", tablet: " << tablet_info.to_string();
159
1
            return Status::OK();
160
1
        }
161
2
    } while (false);
162
163
    /// Step 2: check if there are too many transactions on running.
164
    // check if there are too many transactions on running.
165
    // if yes, reject the request.
166
47.5k
    txn_partition_map_t& txn_partition_map = _get_txn_partition_map(transaction_id);
167
47.5k
    if (txn_partition_map.size() > config::max_runnings_transactions_per_txn_map) {
168
0
        return Status::Error<TOO_MANY_TRANSACTIONS>("too many transactions: {}, limit: {}",
169
0
                                                    txn_tablet_map.size(),
170
0
                                                    config::max_runnings_transactions_per_txn_map);
171
0
    }
172
173
    /// Step 3: Add transaction to engine
174
    // not found load id
175
    // case 1: user start a new txn, rowset = null
176
    // case 2: loading txn from meta env
177
47.5k
    auto load_info = std::make_shared<TabletTxnInfo>(load_id, nullptr, ingest);
178
47.5k
    load_info->prepare();
179
47.5k
    if (!txn_tablet_map.contains(key)) {
180
6.03k
        g_tablet_txn_info_txn_partitions_count << 1;
181
6.03k
    }
182
47.5k
    txn_tablet_map[key][tablet_info] = std::move(load_info);
183
47.5k
    _insert_txn_partition_map_unlocked(transaction_id, partition_id);
184
47.5k
    VLOG_NOTICE << "add transaction to engine successfully."
185
11
                << "partition_id: " << key.first << ", transaction_id: " << key.second
186
11
                << ", tablet: " << tablet_info.to_string();
187
47.5k
    return Status::OK();
188
47.5k
}
189
190
Status TxnManager::commit_txn(TPartitionId partition_id, const Tablet& tablet,
191
                              TTransactionId transaction_id, const PUniqueId& load_id,
192
                              const RowsetSharedPtr& rowset_ptr, PendingRowsetGuard guard,
193
                              bool is_recovery,
194
                              std::shared_ptr<PartialUpdateInfo> partial_update_info,
195
47.4k
                              const RowBinlogTxnInfo& attach_row_binlog) {
196
47.4k
    return commit_txn(tablet.data_dir()->get_meta(), partition_id, transaction_id,
197
47.4k
                      tablet.tablet_id(), tablet.tablet_uid(), load_id, rowset_ptr,
198
47.4k
                      std::move(guard), is_recovery, partial_update_info, attach_row_binlog);
199
47.4k
}
200
201
Status TxnManager::publish_txn(TPartitionId partition_id, const TabletSharedPtr& tablet,
202
                               TTransactionId transaction_id, const Version& version,
203
                               TabletPublishStatistics* stats,
204
                               std::shared_ptr<TabletTxnInfo>& extend_tablet_txn_info,
205
47.0k
                               const int64_t commit_tso) {
206
47.0k
    return publish_txn(tablet->data_dir()->get_meta(), partition_id, transaction_id,
207
47.0k
                       tablet->tablet_id(), tablet->tablet_uid(), version, stats,
208
47.0k
                       extend_tablet_txn_info, commit_tso);
209
47.0k
}
210
211
void TxnManager::abort_txn(TPartitionId partition_id, TTransactionId transaction_id,
212
0
                           TTabletId tablet_id, TabletUid tablet_uid) {
213
0
    pair<int64_t, int64_t> key(partition_id, transaction_id);
214
0
    TabletInfo tablet_info(tablet_id, tablet_uid);
215
216
0
    std::shared_lock txn_rdlock(_get_txn_map_lock(transaction_id));
217
218
0
    auto& txn_tablet_map = _get_txn_tablet_map(transaction_id);
219
0
    auto it = txn_tablet_map.find(key);
220
0
    if (it == txn_tablet_map.end()) {
221
0
        return;
222
0
    }
223
224
0
    auto& tablet_txn_info_map = it->second;
225
0
    auto tablet_txn_info_iter = tablet_txn_info_map.find(tablet_info);
226
0
    if (tablet_txn_info_iter == tablet_txn_info_map.end()) {
227
0
        return;
228
0
    }
229
230
0
    auto& txn_info = tablet_txn_info_iter->second;
231
0
    txn_info->abort();
232
0
}
233
234
// delete the txn from manager if it is not committed(not have a valid rowset)
235
Status TxnManager::rollback_txn(TPartitionId partition_id, const Tablet& tablet,
236
6
                                TTransactionId transaction_id) {
237
6
    return rollback_txn(partition_id, transaction_id, tablet.tablet_id(), tablet.tablet_uid());
238
6
}
239
240
Status TxnManager::delete_txn(TPartitionId partition_id, const TabletSharedPtr& tablet,
241
336
                              TTransactionId transaction_id) {
242
336
    return delete_txn(tablet->data_dir()->get_meta(), partition_id, transaction_id,
243
336
                      tablet->tablet_id(), tablet->tablet_uid());
244
336
}
245
246
void TxnManager::set_txn_related_delete_bitmap(
247
        TPartitionId partition_id, TTransactionId transaction_id, TTabletId tablet_id,
248
        TabletUid tablet_uid, bool unique_key_merge_on_write, DeleteBitmapPtr delete_bitmap,
249
        const RowsetIdUnorderedSet& rowset_ids,
250
33.3k
        std::shared_ptr<PartialUpdateInfo> partial_update_info) {
251
33.3k
    pair<int64_t, int64_t> key(partition_id, transaction_id);
252
33.3k
    TabletInfo tablet_info(tablet_id, tablet_uid);
253
254
33.3k
    std::lock_guard<std::shared_mutex> txn_lock(_get_txn_lock(transaction_id));
255
33.3k
    {
256
        // get tx
257
33.3k
        std::lock_guard<std::shared_mutex> wrlock(_get_txn_map_lock(transaction_id));
258
33.3k
        txn_tablet_map_t& txn_tablet_map = _get_txn_tablet_map(transaction_id);
259
33.3k
        auto it = txn_tablet_map.find(key);
260
33.3k
        if (it == txn_tablet_map.end()) {
261
1
            LOG(WARNING) << "transaction_id: " << transaction_id
262
1
                         << " partition_id: " << partition_id << " may be cleared";
263
1
            return;
264
1
        }
265
33.3k
        auto load_itr = it->second.find(tablet_info);
266
33.3k
        if (load_itr == it->second.end()) {
267
0
            LOG(WARNING) << "transaction_id: " << transaction_id
268
0
                         << " partition_id: " << partition_id << " tablet_id: " << tablet_id
269
0
                         << " may be cleared";
270
0
            return;
271
0
        }
272
33.3k
        auto& load_info = load_itr->second;
273
33.3k
        load_info->unique_key_merge_on_write = unique_key_merge_on_write;
274
33.3k
        load_info->delete_bitmap = delete_bitmap;
275
33.3k
        load_info->rowset_ids = rowset_ids;
276
33.3k
        load_info->partial_update_info = partial_update_info;
277
33.3k
    }
278
33.3k
}
279
280
Status TxnManager::commit_txn(OlapMeta* meta, TPartitionId partition_id,
281
                              TTransactionId transaction_id, TTabletId tablet_id,
282
                              TabletUid tablet_uid, const PUniqueId& load_id,
283
                              const RowsetSharedPtr& rowset_ptr, PendingRowsetGuard guard,
284
                              bool is_recovery,
285
                              std::shared_ptr<PartialUpdateInfo> partial_update_info,
286
47.6k
                              const RowBinlogTxnInfo& attach_row_binlog) {
287
47.6k
    if (partition_id < 1 || transaction_id < 1 || tablet_id < 1) {
288
0
        LOG(WARNING) << "invalid commit req "
289
0
                     << " partition_id=" << partition_id << " transaction_id=" << transaction_id
290
0
                     << " tablet_id=" << tablet_id;
291
0
        return Status::InternalError("invalid partition id");
292
0
    }
293
294
47.6k
    pair<int64_t, int64_t> key(partition_id, transaction_id);
295
47.6k
    TabletInfo tablet_info(tablet_id, tablet_uid);
296
47.6k
    if (rowset_ptr == nullptr) {
297
0
        return Status::Error<ROWSET_INVALID>(
298
0
                "could not commit txn because rowset ptr is null. partition_id: {}, "
299
0
                "transaction_id: {}, tablet: {}",
300
0
                key.first, key.second, tablet_info.to_string());
301
0
    }
302
303
47.6k
    DBUG_EXECUTE_IF("TxnManager.commit_txn.random_failed", {
304
47.6k
        if (rand() % 100 < (100 * dp->param("percent", 0.5))) {
305
47.6k
            LOG_WARNING("TxnManager.commit_txn.random_failed")
306
47.6k
                    .tag("txn_id", transaction_id)
307
47.6k
                    .tag("tablet_id", tablet_id);
308
47.6k
            return Status::InternalError("debug commit txn random failed");
309
47.6k
        }
310
47.6k
    });
311
47.6k
    DBUG_EXECUTE_IF("TxnManager.commit_txn.wait", {
312
47.6k
        if (auto wait = dp->param<int>("duration", 0); wait > 0) {
313
47.6k
            LOG_WARNING("TxnManager.commit_txn.wait")
314
47.6k
                    .tag("txn_id", transaction_id)
315
47.6k
                    .tag("tablet_id", tablet_id)
316
47.6k
                    .tag("wait ms", wait);
317
47.6k
            std::this_thread::sleep_for(std::chrono::milliseconds(wait));
318
47.6k
        }
319
47.6k
    });
320
321
47.6k
    std::lock_guard<std::shared_mutex> txn_lock(_get_txn_lock(transaction_id));
322
    // this while loop just run only once, just for if break
323
47.6k
    do {
324
        // get tx
325
47.6k
        std::shared_lock rdlock(_get_txn_map_lock(transaction_id));
326
47.6k
        auto rs_pb = rowset_ptr->rowset_meta()->get_rowset_pb();
327
        // TODO(dx): remove log after fix partition id eq 0 bug
328
47.6k
        if (!rs_pb.has_partition_id() || rs_pb.partition_id() == 0) {
329
1
            rowset_ptr->rowset_meta()->set_partition_id(partition_id);
330
1
            LOG(WARNING) << "cant get partition id from rs pb, get from func arg partition_id="
331
1
                         << partition_id;
332
1
        }
333
47.6k
        txn_tablet_map_t& txn_tablet_map = _get_txn_tablet_map(transaction_id);
334
47.6k
        auto it = txn_tablet_map.find(key);
335
47.6k
        if (it == txn_tablet_map.end()) {
336
30
            break;
337
30
        }
338
339
47.6k
        auto load_itr = it->second.find(tablet_info);
340
47.6k
        if (load_itr == it->second.end()) {
341
116
            break;
342
116
        }
343
344
        // found load for txn,tablet
345
        // case 1: user commit rowset, then the load id must be equal
346
47.5k
        auto& load_info = load_itr->second;
347
        // check if load id is equal
348
47.5k
        if (load_info->rowset == nullptr) {
349
47.5k
            break;
350
47.5k
        }
351
352
2
        if (load_info->load_id.hi() != load_id.hi() || load_info->load_id.lo() != load_id.lo()) {
353
0
            break;
354
0
        }
355
356
        // find a rowset with same rowset id, then it means a duplicate call
357
2
        if (load_info->rowset->rowset_id() == rowset_ptr->rowset_id()) {
358
1
            LOG(INFO) << "find rowset exists when commit transaction to engine."
359
1
                      << "partition_id: " << key.first << ", transaction_id: " << key.second
360
1
                      << ", tablet: " << tablet_info.to_string()
361
1
                      << ", rowset_id: " << load_info->rowset->rowset_id();
362
            // Should not remove this rowset from pending rowsets
363
1
            load_info->pending_rs_guard = std::move(guard);
364
1
            return Status::OK();
365
1
        }
366
367
        // find a rowset with different rowset id, then it should not happen, just return errors
368
1
        return Status::Error<PUSH_TRANSACTION_ALREADY_EXIST>(
369
1
                "find rowset exists when commit transaction to engine. but rowset ids are not "
370
1
                "same. partition_id: {}, transaction_id: {}, tablet: {}, exist rowset_id: {}, new "
371
1
                "rowset_id: {}",
372
1
                key.first, key.second, tablet_info.to_string(),
373
1
                load_info->rowset->rowset_id().to_string(), rowset_ptr->rowset_id().to_string());
374
2
    } while (false);
375
376
    // if not in recovery mode, then should persist the meta to meta env
377
    // save meta need access disk, it maybe very slow, so that it is not in global txn lock
378
    // it is under a single txn lock
379
47.6k
    if (!is_recovery) {
380
47.5k
        std::optional<BinlogFormatPB> binlog_format;
381
47.5k
        std::optional<RowsetMetaPB> attach_row_binlog_rowset_meta;
382
47.5k
        if (attach_row_binlog.rowset != nullptr) {
383
2
            attach_row_binlog_rowset_meta =
384
2
                    attach_row_binlog.rowset->rowset_meta()->get_rowset_pb();
385
2
            binlog_format = BinlogFormatPB::ROW;
386
2
        }
387
47.5k
        Status save_status = RowsetMetaManager::save(meta, tablet_uid, rowset_ptr->rowset_id(),
388
47.5k
                                                     rowset_ptr->rowset_meta()->get_rowset_pb(),
389
47.5k
                                                     binlog_format, attach_row_binlog_rowset_meta);
390
47.5k
        DBUG_EXECUTE_IF("TxnManager.RowsetMetaManager.save_wait", {
391
47.5k
            if (auto wait = dp->param<int>("duration", 0); wait > 0) {
392
47.5k
                LOG_WARNING("TxnManager.RowsetMetaManager.save_wait")
393
47.5k
                        .tag("txn_id", transaction_id)
394
47.5k
                        .tag("tablet_id", tablet_id)
395
47.5k
                        .tag("wait ms", wait);
396
47.5k
                std::this_thread::sleep_for(std::chrono::milliseconds(wait));
397
47.5k
            }
398
47.5k
        });
399
47.5k
        if (!save_status.ok()) {
400
0
            save_status.append(fmt::format(", txn id: {}", transaction_id));
401
0
            return save_status;
402
0
        }
403
404
47.5k
        if (partial_update_info && partial_update_info->is_partial_update()) {
405
492
            PartialUpdateInfoPB partial_update_info_pb;
406
492
            partial_update_info->to_pb(&partial_update_info_pb);
407
492
            save_status = RowsetMetaManager::save_partial_update_info(
408
492
                    meta, tablet_id, partition_id, transaction_id, partial_update_info_pb);
409
492
            if (!save_status.ok()) {
410
0
                save_status.append(fmt::format(", txn_id: {}", transaction_id));
411
0
                return save_status;
412
0
            }
413
492
        }
414
47.5k
    }
415
416
47.6k
    TabletSharedPtr tablet;
417
47.6k
    std::shared_ptr<PartialUpdateInfo> decoded_partial_update_info {nullptr};
418
47.6k
    if (is_recovery) {
419
134
        tablet = _engine.tablet_manager()->get_tablet(tablet_id, tablet_uid);
420
134
        if (tablet != nullptr && tablet->enable_unique_key_merge_on_write()) {
421
6
            PartialUpdateInfoPB partial_update_info_pb;
422
6
            auto st = RowsetMetaManager::try_get_partial_update_info(
423
6
                    meta, tablet_id, partition_id, transaction_id, &partial_update_info_pb);
424
6
            if (st.ok()) {
425
0
                decoded_partial_update_info = std::make_shared<PartialUpdateInfo>();
426
0
                decoded_partial_update_info->from_pb(&partial_update_info_pb);
427
0
                DCHECK(decoded_partial_update_info->is_partial_update());
428
6
            } else if (!st.is<META_KEY_NOT_FOUND>()) {
429
                // the load is not a partial update
430
0
                return st;
431
0
            }
432
6
        }
433
134
    }
434
435
47.6k
    {
436
47.6k
        std::lock_guard<std::shared_mutex> wrlock(_get_txn_map_lock(transaction_id));
437
47.6k
        auto load_info = std::make_shared<TabletTxnInfo>(load_id, rowset_ptr);
438
47.6k
        load_info->attach_row_binlog = attach_row_binlog;
439
        // resolve the independent binlog tablet in advance for the later publish phase.
440
47.6k
        if (load_info->attach_row_binlog.rowset != nullptr &&
441
47.6k
            load_info->attach_row_binlog.tablet == nullptr) {
442
2
            int64_t binlog_tablet_id =
443
2
                    load_info->attach_row_binlog.rowset->rowset_meta()->tablet_id();
444
2
            load_info->attach_row_binlog.tablet =
445
2
                    _engine.tablet_manager()->get_tablet(binlog_tablet_id);
446
2
            if (load_info->attach_row_binlog.tablet == nullptr) {
447
0
                return Status::Error<TABLE_NOT_FOUND>(
448
0
                        "binlog tablet not found when commit txn, binlog_tablet_id={}, txn_id={}",
449
0
                        binlog_tablet_id, transaction_id);
450
0
            }
451
2
        }
452
47.6k
        load_info->pending_rs_guard = std::move(guard);
453
47.6k
        if (is_recovery) {
454
134
            if (tablet != nullptr && tablet->enable_unique_key_merge_on_write()) {
455
6
                load_info->unique_key_merge_on_write = true;
456
6
                load_info->delete_bitmap.reset(new DeleteBitmap(tablet->tablet_id()));
457
6
                if (decoded_partial_update_info) {
458
0
                    LOG_INFO(
459
0
                            "get partial update info from RocksDB during recovery. txn_id={}, "
460
0
                            "partition_id={}, tablet_id={}, partial_update_info=[{}]",
461
0
                            transaction_id, partition_id, tablet_id,
462
0
                            decoded_partial_update_info->summary());
463
0
                    load_info->partial_update_info = decoded_partial_update_info;
464
0
                }
465
6
            }
466
134
        }
467
468
        // For binlog<Row> txn, the binlog delete bitmap is only needed in the publish phase to
469
        // copy delete bitmap deltas onto the independent binlog tablet.
470
47.6k
        if (load_info->attach_row_binlog.rowset != nullptr) {
471
2
            TabletSharedPtr t = _engine.tablet_manager()->get_tablet(tablet_id, tablet_uid);
472
2
            if (t != nullptr && t->enable_unique_key_merge_on_write()) {
473
0
                load_info->attach_row_binlog.delete_bitmap.reset(
474
0
                        new DeleteBitmap(load_info->attach_row_binlog.tablet->tablet_id()));
475
0
            }
476
2
        }
477
47.6k
        load_info->commit();
478
479
47.6k
        txn_tablet_map_t& txn_tablet_map = _get_txn_tablet_map(transaction_id);
480
47.6k
        txn_tablet_map[key][tablet_info] = std::move(load_info);
481
47.6k
        _insert_txn_partition_map_unlocked(transaction_id, partition_id);
482
47.6k
        VLOG_NOTICE << "commit transaction to engine successfully."
483
17
                    << " partition_id: " << key.first << ", transaction_id: " << key.second
484
17
                    << ", tablet: " << tablet_info.to_string()
485
17
                    << ", rowsetid: " << rowset_ptr->rowset_id()
486
17
                    << ", version: " << rowset_ptr->version().first;
487
47.6k
    }
488
0
    return Status::OK();
489
47.6k
}
490
491
// remove a txn from txn manager
492
Status TxnManager::publish_txn(OlapMeta* meta, TPartitionId partition_id,
493
                               TTransactionId transaction_id, TTabletId tablet_id,
494
                               TabletUid tablet_uid, const Version& version,
495
                               TabletPublishStatistics* stats,
496
                               std::shared_ptr<TabletTxnInfo>& extend_tablet_txn_info,
497
47.1k
                               const int64_t commit_tso) {
498
47.1k
    auto tablet = _engine.tablet_manager()->get_tablet(tablet_id);
499
47.1k
    if (tablet == nullptr) {
500
0
        return Status::OK();
501
0
    }
502
47.1k
    DCHECK(stats != nullptr);
503
504
47.1k
    pair<int64_t, int64_t> key(partition_id, transaction_id);
505
47.1k
    TabletInfo tablet_info(tablet_id, tablet_uid);
506
47.1k
    RowsetSharedPtr rowset;
507
47.1k
    std::shared_ptr<TabletTxnInfo> tablet_txn_info;
508
47.1k
    int64_t t1 = MonotonicMicros();
509
    /// Step 1: get rowset, tablet_txn_info by key
510
47.1k
    {
511
47.1k
        std::shared_lock txn_rlock(_get_txn_lock(transaction_id));
512
47.1k
        std::shared_lock txn_map_rlock(_get_txn_map_lock(transaction_id));
513
47.1k
        stats->lock_wait_time_us += MonotonicMicros() - t1;
514
515
47.1k
        txn_tablet_map_t& txn_tablet_map = _get_txn_tablet_map(transaction_id);
516
47.1k
        if (auto it = txn_tablet_map.find(key); it != txn_tablet_map.end()) {
517
47.1k
            auto& tablet_map = it->second;
518
47.1k
            if (auto txn_info_iter = tablet_map.find(tablet_info);
519
47.1k
                txn_info_iter != tablet_map.end()) {
520
                // found load for txn,tablet
521
                // case 1: user commit rowset, then the load id must be equal
522
47.1k
                tablet_txn_info = txn_info_iter->second;
523
47.1k
                extend_tablet_txn_info = tablet_txn_info;
524
47.1k
                rowset = tablet_txn_info->rowset;
525
47.1k
            }
526
47.1k
        }
527
47.1k
    }
528
47.1k
    if (rowset == nullptr) {
529
1
        return Status::Error<TRANSACTION_NOT_EXIST>(
530
1
                "publish txn failed, rowset not found. partition_id={}, transaction_id={}, "
531
1
                "tablet={}, commit_tso={}",
532
1
                partition_id, transaction_id, tablet_info.to_string(), commit_tso);
533
1
    }
534
47.1k
    DBUG_EXECUTE_IF("TxnManager.publish_txn.random_failed_before_save_rs_meta", {
535
47.1k
        if (rand() % 100 < (100 * dp->param("percent", 0.5))) {
536
47.1k
            LOG_WARNING("TxnManager.publish_txn.random_failed_before_save_rs_meta")
537
47.1k
                    .tag("txn_id", transaction_id)
538
47.1k
                    .tag("tablet_id", tablet_id);
539
47.1k
            return Status::InternalError("debug publish txn before save rs meta random failed");
540
47.1k
        }
541
47.1k
    });
542
47.1k
    DBUG_EXECUTE_IF("TxnManager.publish_txn.wait_before_save_rs_meta", {
543
47.1k
        if (auto wait = dp->param<int>("duration", 0); wait > 0) {
544
47.1k
            LOG_WARNING("TxnManager.publish_txn.wait_before_save_rs_meta")
545
47.1k
                    .tag("txn_id", transaction_id)
546
47.1k
                    .tag("tablet_id", tablet_id)
547
47.1k
                    .tag("wait ms", wait);
548
47.1k
            std::this_thread::sleep_for(std::chrono::milliseconds(wait));
549
47.1k
        }
550
47.1k
    });
551
552
    /// Step 2: make rowset visible
553
    // save meta need access disk, it maybe very slow, so that it is not in global txn lock
554
    // it is under a single txn lock
555
    // TODO(ygl): rowset is already set version here, memory is changed, if save failed
556
    // it maybe a fatal error
557
47.1k
    rowset->make_visible(version, commit_tso);
558
559
    // Make the attached binlog rowset visible together.
560
47.1k
    if (tablet_txn_info->attach_row_binlog.rowset != nullptr) {
561
1
        tablet_txn_info->attach_row_binlog.rowset->make_visible(version, commit_tso);
562
1
    }
563
564
47.1k
    DBUG_EXECUTE_IF("TxnManager.publish_txn.random_failed_after_save_rs_meta", {
565
47.1k
        if (rand() % 100 < (100 * dp->param("percent", 0.5))) {
566
47.1k
            LOG_WARNING("TxnManager.publish_txn.random_failed_after_save_rs_meta")
567
47.1k
                    .tag("txn_id", transaction_id)
568
47.1k
                    .tag("tablet_id", tablet_id);
569
47.1k
            return Status::InternalError("debug publish txn after save rs meta random failed");
570
47.1k
        }
571
47.1k
    });
572
47.1k
    DBUG_EXECUTE_IF("TxnManager.publish_txn.wait_after_save_rs_meta", {
573
47.1k
        if (auto wait = dp->param<int>("duration", 0); wait > 0) {
574
47.1k
            LOG_WARNING("TxnManager.publish_txn.wait_after_save_rs_meta")
575
47.1k
                    .tag("txn_id", transaction_id)
576
47.1k
                    .tag("tablet_id", tablet_id)
577
47.1k
                    .tag("wait ms", wait);
578
47.1k
            std::this_thread::sleep_for(std::chrono::milliseconds(wait));
579
47.1k
        }
580
47.1k
    });
581
    // update delete_bitmap
582
47.1k
    if (tablet_txn_info->unique_key_merge_on_write) {
583
33.1k
        int64_t t2 = MonotonicMicros();
584
33.1k
        if (rowset->num_segments() > 1 &&
585
33.1k
            !tablet_txn_info->delete_bitmap->has_calculated_for_multi_segments(
586
0
                    rowset->rowset_id())) {
587
            // delete bitmap is empty, should re-calculate delete bitmaps between segments
588
0
            std::vector<segment_v2::SegmentSharedPtr> segments;
589
0
            RETURN_IF_ERROR(std::static_pointer_cast<BetaRowset>(rowset)->load_segments(&segments));
590
0
            RETURN_IF_ERROR(tablet->calc_delete_bitmap_between_segments(
591
0
                    rowset->tablet_schema(), rowset->rowset_id(), segments,
592
0
                    tablet_txn_info->delete_bitmap));
593
0
        }
594
595
33.1k
        RETURN_IF_ERROR(
596
33.1k
                Tablet::update_delete_bitmap(tablet, tablet_txn_info.get(), transaction_id));
597
33.1k
        int64_t t3 = MonotonicMicros();
598
33.1k
        stats->calc_delete_bitmap_time_us = t3 - t2;
599
33.1k
        RETURN_IF_ERROR(TabletMetaManager::save_delete_bitmap(
600
33.1k
                tablet->data_dir(), tablet->tablet_id(), tablet_txn_info->delete_bitmap,
601
33.1k
                version.second));
602
33.1k
        if (tablet_txn_info->attach_row_binlog.rowset != nullptr) {
603
0
            DCHECK(tablet_txn_info->attach_row_binlog.tablet != nullptr);
604
0
            if (tablet_txn_info->attach_row_binlog.delete_bitmap != nullptr) {
605
0
                auto binlog_tablet =
606
0
                        std::static_pointer_cast<Tablet>(tablet_txn_info->attach_row_binlog.tablet);
607
0
                binlog_tablet->merge_delete_bitmap(
608
0
                        *tablet_txn_info->attach_row_binlog.delete_bitmap);
609
0
                RETURN_IF_ERROR(TabletMetaManager::save_delete_bitmap(
610
0
                        binlog_tablet->data_dir(), binlog_tablet->tablet_id(),
611
0
                        tablet_txn_info->attach_row_binlog.delete_bitmap, version.second));
612
0
            }
613
0
        }
614
33.1k
        stats->save_meta_time_us = MonotonicMicros() - t3;
615
33.1k
    }
616
617
    /// Step 3:  add to binlog
618
47.1k
    std::optional<BinlogFormatPB> binlog_format;
619
47.1k
    if (tablet->enable_ccr_binlog()) {
620
0
        binlog_format = BinlogFormatPB::STATEMENT_AND_SNAPSHOT;
621
0
        auto status = rowset->add_to_binlog();
622
0
        if (!status.ok()) {
623
0
            return Status::Error<ROWSET_ADD_TO_BINLOG_FAILED>(
624
0
                    "add rowset to binlog failed. when publish txn rowset_id: {}, tablet id: {}, "
625
0
                    "txn id: {}, status: {}",
626
0
                    rowset->rowset_id().to_string(), tablet_id, transaction_id,
627
0
                    status.to_string_no_stack());
628
0
        }
629
0
    }
630
631
47.1k
    std::optional<RowsetMetaPB> attach_row_binlog_rowset_meta;
632
47.1k
    if (tablet_txn_info->attach_row_binlog.rowset != nullptr) {
633
1
        attach_row_binlog_rowset_meta =
634
1
                tablet_txn_info->attach_row_binlog.rowset->rowset_meta()->get_rowset_pb();
635
1
        binlog_format = BinlogFormatPB::ROW;
636
1
    }
637
638
    /// Step 4: save meta
639
47.1k
    int64_t t5 = MonotonicMicros();
640
47.1k
    auto status = RowsetMetaManager::save(meta, tablet_uid, rowset->rowset_id(),
641
47.1k
                                          rowset->rowset_meta()->get_rowset_pb(), binlog_format,
642
47.1k
                                          attach_row_binlog_rowset_meta);
643
47.1k
    stats->save_meta_time_us += MonotonicMicros() - t5;
644
47.1k
    if (!status.ok()) {
645
0
        status.append(fmt::format(", txn id: {}", transaction_id));
646
0
        return status;
647
0
    }
648
649
47.1k
    if (tablet_txn_info->unique_key_merge_on_write && tablet_txn_info->partial_update_info &&
650
47.1k
        tablet_txn_info->partial_update_info->is_partial_update()) {
651
492
        status = RowsetMetaManager::remove_partial_update_info(meta, tablet_id, partition_id,
652
492
                                                               transaction_id);
653
492
        if (!status) {
654
            // discard the error status and print the warning log
655
0
            LOG_WARNING(
656
0
                    "fail to remove partial update info from RocksDB. txn_id={}, rowset_id={}, "
657
0
                    "tablet_id={}, tablet_uid={}",
658
0
                    transaction_id, rowset->rowset_id().to_string(), tablet_id,
659
0
                    tablet_uid.to_string());
660
0
        }
661
492
    }
662
663
    // TODO(Drogon): remove these test codes
664
47.1k
    if (tablet->enable_ccr_binlog()) {
665
0
        auto version_str = fmt::format("{}", version.first);
666
0
        VLOG_DEBUG << fmt::format("tabletid: {}, version: {}, binlog filepath: {}", tablet_id,
667
0
                                  version_str, tablet->get_binlog_filepath(version_str));
668
0
    }
669
670
    /// Step 5: remove tablet_info from tnx_tablet_map
671
    // txn_tablet_map[key] empty, remove key from txn_tablet_map
672
47.1k
    int64_t t6 = MonotonicMicros();
673
47.1k
    std::lock_guard<std::shared_mutex> txn_lock(_get_txn_lock(transaction_id));
674
47.1k
    std::lock_guard<std::shared_mutex> wrlock(_get_txn_map_lock(transaction_id));
675
47.1k
    stats->lock_wait_time_us += MonotonicMicros() - t6;
676
47.1k
    _remove_txn_tablet_info_unlocked(partition_id, transaction_id, tablet_id, tablet_uid, txn_lock,
677
47.1k
                                     wrlock);
678
18.4E
    VLOG_NOTICE << "publish txn successfully."
679
18.4E
                << " partition_id: " << key.first << ", txn_id: " << key.second
680
18.4E
                << ", tablet_id: " << tablet_info.tablet_id << ", rowsetid: " << rowset->rowset_id()
681
18.4E
                << ", version: " << version.first << "," << version.second;
682
47.1k
    return status;
683
47.1k
}
684
685
void TxnManager::_remove_txn_tablet_info_unlocked(TPartitionId partition_id,
686
                                                  TTransactionId transaction_id,
687
                                                  TTabletId tablet_id, TabletUid tablet_uid,
688
                                                  std::lock_guard<std::shared_mutex>& txn_lock,
689
47.1k
                                                  std::lock_guard<std::shared_mutex>& wrlock) {
690
47.1k
    std::pair<int64_t, int64_t> key {partition_id, transaction_id};
691
47.1k
    TabletInfo tablet_info {tablet_id, tablet_uid};
692
47.1k
    txn_tablet_map_t& txn_tablet_map = _get_txn_tablet_map(transaction_id);
693
47.1k
    if (auto it = txn_tablet_map.find(key); it != txn_tablet_map.end()) {
694
47.1k
        it->second.erase(tablet_info);
695
47.1k
        if (it->second.empty()) {
696
5.99k
            txn_tablet_map.erase(it);
697
5.99k
            g_tablet_txn_info_txn_partitions_count << -1;
698
5.99k
            _clear_txn_partition_map_unlocked(transaction_id, partition_id);
699
5.99k
        }
700
47.1k
    }
701
47.1k
}
702
703
void TxnManager::remove_txn_tablet_info(TPartitionId partition_id, TTransactionId transaction_id,
704
0
                                        TTabletId tablet_id, TabletUid tablet_uid) {
705
0
    std::lock_guard<std::shared_mutex> txn_lock(_get_txn_lock(transaction_id));
706
0
    std::lock_guard<std::shared_mutex> wrlock(_get_txn_map_lock(transaction_id));
707
0
    _remove_txn_tablet_info_unlocked(partition_id, transaction_id, tablet_id, tablet_uid, txn_lock,
708
0
                                     wrlock);
709
0
}
710
711
// txn could be rollbacked if it does not have related rowset
712
// if the txn has related rowset then could not rollback it, because it
713
// may be committed in another thread and our current thread meets errors when writing to data file
714
// BE has to wait for fe call clear txn api
715
Status TxnManager::rollback_txn(TPartitionId partition_id, TTransactionId transaction_id,
716
8
                                TTabletId tablet_id, TabletUid tablet_uid) {
717
8
    pair<int64_t, int64_t> key(partition_id, transaction_id);
718
8
    TabletInfo tablet_info(tablet_id, tablet_uid);
719
720
8
    std::lock_guard<std::shared_mutex> wrlock(_get_txn_map_lock(transaction_id));
721
8
    txn_tablet_map_t& txn_tablet_map = _get_txn_tablet_map(transaction_id);
722
723
8
    auto it = txn_tablet_map.find(key);
724
8
    if (it == txn_tablet_map.end()) {
725
0
        return Status::OK();
726
0
    }
727
728
8
    auto& tablet_txn_info_map = it->second;
729
8
    if (auto load_itr = tablet_txn_info_map.find(tablet_info);
730
8
        load_itr != tablet_txn_info_map.end()) {
731
        // found load for txn,tablet
732
        // case 1: user commit rowset, then the load id must be equal
733
8
        const auto& load_info = load_itr->second;
734
8
        if (load_info->rowset != nullptr) {
735
1
            return Status::Error<TRANSACTION_ALREADY_COMMITTED>(
736
1
                    "if rowset is not null, it means other thread may commit the rowset should "
737
1
                    "not delete txn any more");
738
1
        }
739
8
    }
740
741
7
    tablet_txn_info_map.erase(tablet_info);
742
7
    LOG(INFO) << "rollback transaction from engine successfully."
743
7
              << " partition_id: " << key.first << ", transaction_id: " << key.second
744
7
              << ", tablet: " << tablet_info.to_string();
745
7
    if (tablet_txn_info_map.empty()) {
746
7
        txn_tablet_map.erase(it);
747
7
        g_tablet_txn_info_txn_partitions_count << -1;
748
7
        _clear_txn_partition_map_unlocked(transaction_id, partition_id);
749
7
    }
750
7
    return Status::OK();
751
8
}
752
753
// fe call this api to clear unused rowsets in be
754
// could not delete the rowset if it already has a valid version
755
Status TxnManager::delete_txn(OlapMeta* meta, TPartitionId partition_id,
756
                              TTransactionId transaction_id, TTabletId tablet_id,
757
341
                              TabletUid tablet_uid) {
758
341
    pair<int64_t, int64_t> key(partition_id, transaction_id);
759
341
    TabletInfo tablet_info(tablet_id, tablet_uid);
760
341
    std::lock_guard<std::shared_mutex> txn_wrlock(_get_txn_map_lock(transaction_id));
761
341
    txn_tablet_map_t& txn_tablet_map = _get_txn_tablet_map(transaction_id);
762
341
    auto it = txn_tablet_map.find(key);
763
341
    if (it == txn_tablet_map.end()) {
764
0
        return Status::Error<TRANSACTION_NOT_EXIST>("key not founded from txn_tablet_map");
765
0
    }
766
341
    Status st = Status::OK();
767
341
    auto load_itr = it->second.find(tablet_info);
768
341
    if (load_itr != it->second.end()) {
769
        // found load for txn,tablet
770
        // case 1: user commit rowset, then the load id must be equal
771
341
        auto& load_info = load_itr->second;
772
341
        auto& rowset = load_info->rowset;
773
341
        if (rowset != nullptr && meta != nullptr) {
774
340
            if (!rowset->is_pending()) {
775
1
                st = Status::Error<TRANSACTION_ALREADY_COMMITTED>(
776
1
                        "could not delete transaction from engine, just remove it from memory not "
777
1
                        "delete from disk, because related rowset already published. partition_id: "
778
1
                        "{}, transaction_id: {}, tablet: {}, rowset id: {}, version: {}, state: {}",
779
1
                        key.first, key.second, tablet_info.to_string(),
780
1
                        rowset->rowset_id().to_string(), rowset->version().to_string(),
781
1
                        RowsetStatePB_Name(rowset->rowset_meta_state()));
782
339
            } else {
783
339
                const auto& attach_binlog_rowset = load_info->attach_row_binlog.rowset;
784
339
                if (attach_binlog_rowset != nullptr) {
785
1
                    static_cast<void>(RowsetMetaManager::remove(
786
1
                            meta, attach_binlog_rowset->rowset_meta()->tablet_uid(),
787
1
                            attach_binlog_rowset->rowset_id()));
788
1
                    _engine.add_unused_rowset(attach_binlog_rowset);
789
1
                }
790
339
                static_cast<void>(RowsetMetaManager::remove(meta, tablet_uid, rowset->rowset_id()));
791
339
#ifndef BE_TEST
792
339
                _engine.add_unused_rowset(rowset);
793
339
#endif
794
339
                VLOG_NOTICE << "delete transaction from engine successfully."
795
3
                            << " partition_id: " << key.first << ", transaction_id: " << key.second
796
3
                            << ", tablet: " << tablet_info.to_string() << ", rowset: "
797
3
                            << (rowset != nullptr ? rowset->rowset_id().to_string() : "0")
798
3
                            << ", binlog<row> rowset: "
799
3
                            << (attach_binlog_rowset != nullptr
800
3
                                        ? attach_binlog_rowset->rowset_id().to_string()
801
3
                                        : "0");
802
339
            }
803
340
        }
804
341
        it->second.erase(load_itr);
805
341
    }
806
341
    if (it->second.empty()) {
807
21
        txn_tablet_map.erase(it);
808
21
        g_tablet_txn_info_txn_partitions_count << -1;
809
21
        _clear_txn_partition_map_unlocked(transaction_id, partition_id);
810
21
    }
811
341
    return st;
812
341
}
813
814
void TxnManager::get_tablet_related_txns(TTabletId tablet_id, TabletUid tablet_uid,
815
                                         int64_t* partition_id,
816
6
                                         std::set<int64_t>* transaction_ids) {
817
6
    if (partition_id == nullptr || transaction_ids == nullptr) {
818
0
        LOG(WARNING) << "parameter is null when get transactions by tablet";
819
0
        return;
820
0
    }
821
822
6
    TabletInfo tablet_info(tablet_id, tablet_uid);
823
12
    for (int32_t i = 0; i < _txn_map_shard_size; i++) {
824
6
        std::shared_lock txn_rdlock(_txn_map_locks[i]);
825
6
        txn_tablet_map_t& txn_tablet_map = _txn_tablet_maps[i];
826
6
        for (auto& it : txn_tablet_map) {
827
1
            if (it.second.find(tablet_info) != it.second.end()) {
828
1
                *partition_id = it.first.first;
829
1
                transaction_ids->insert(it.first.second);
830
1
                VLOG_NOTICE << "find transaction on tablet."
831
1
                            << "partition_id: " << it.first.first
832
1
                            << ", transaction_id: " << it.first.second
833
1
                            << ", tablet: " << tablet_info.to_string();
834
1
            }
835
1
        }
836
6
    }
837
6
}
838
839
// force drop all txns related with the tablet
840
// maybe lock error, because not get txn lock before remove from meta
841
void TxnManager::force_rollback_tablet_related_txns(OlapMeta* meta, TTabletId tablet_id,
842
5.72k
                                                    TabletUid tablet_uid) {
843
5.72k
    TabletInfo tablet_info(tablet_id, tablet_uid);
844
5.64M
    for (int32_t i = 0; i < _txn_map_shard_size; i++) {
845
5.63M
        std::lock_guard<std::shared_mutex> txn_wrlock(_txn_map_locks[i]);
846
5.63M
        txn_tablet_map_t& txn_tablet_map = _txn_tablet_maps[i];
847
5.65M
        for (auto it = txn_tablet_map.begin(); it != txn_tablet_map.end();) {
848
15.1k
            auto load_itr = it->second.find(tablet_info);
849
15.1k
            if (load_itr != it->second.end()) {
850
22
                auto& load_info = load_itr->second;
851
22
                auto& rowset = load_info->rowset;
852
22
                const auto& attach_binlog_rowset = load_info->attach_row_binlog.rowset;
853
22
                if (rowset != nullptr && meta != nullptr) {
854
22
                    LOG(INFO) << " delete transaction from engine "
855
22
                              << ", tablet: " << tablet_info.to_string()
856
22
                              << ", rowset id: " << rowset->rowset_id();
857
                    // clean the attached binlog rowset first
858
22
                    if (attach_binlog_rowset != nullptr) {
859
0
                        Status status = RowsetMetaManager::remove(
860
0
                                meta, attach_binlog_rowset->rowset_meta()->tablet_uid(),
861
0
                                attach_binlog_rowset->rowset_id());
862
0
                        if (!status.ok() && !status.is<META_KEY_NOT_FOUND>()) {
863
0
                            LOG(WARNING)
864
0
                                    << "failed to remove binlog<row> rowset meta, rowset_id="
865
0
                                    << attach_binlog_rowset->rowset_id() << ", status=" << status;
866
0
                        }
867
0
                    }
868
22
                    static_cast<void>(
869
22
                            RowsetMetaManager::remove(meta, tablet_uid, rowset->rowset_id()));
870
22
                }
871
22
                LOG(INFO) << "remove tablet related txn."
872
22
                          << " partition_id: " << it->first.first
873
22
                          << ", transaction_id: " << it->first.second
874
22
                          << ", tablet: " << tablet_info.to_string() << ", rowset: "
875
22
                          << (rowset != nullptr ? rowset->rowset_id().to_string() : "0")
876
22
                          << ", binlog<row> rowset: "
877
22
                          << (attach_binlog_rowset != nullptr
878
22
                                      ? attach_binlog_rowset->rowset_id().to_string()
879
22
                                      : "0");
880
22
                it->second.erase(load_itr);
881
22
            }
882
15.1k
            if (it->second.empty()) {
883
4
                _clear_txn_partition_map_unlocked(it->first.second, it->first.first);
884
4
                it = txn_tablet_map.erase(it);
885
4
                g_tablet_txn_info_txn_partitions_count << -1;
886
15.1k
            } else {
887
15.1k
                ++it;
888
15.1k
            }
889
15.1k
        }
890
5.63M
    }
891
5.72k
    if (meta != nullptr) {
892
5.71k
        Status st = RowsetMetaManager::remove_tablet_related_partial_update_info(meta, tablet_id);
893
5.71k
        if (!st.ok()) {
894
0
            LOG_WARNING("failed to partial update info, tablet_id={}, err={}", tablet_id,
895
0
                        st.to_string());
896
0
        }
897
5.71k
    }
898
5.72k
}
899
900
void TxnManager::get_txn_related_tablets(
901
        const TTransactionId transaction_id, TPartitionId partition_id,
902
        std::map<TabletInfo, RowsetSharedPtr>* tablet_infos,
903
6.03k
        std::map<TabletInfo, std::shared_ptr<TabletTxnInfo>>* tablet_txn_infos) {
904
    // get tablets in this transaction
905
6.03k
    pair<int64_t, int64_t> key(partition_id, transaction_id);
906
6.03k
    std::shared_lock txn_rdlock(_get_txn_map_lock(transaction_id));
907
6.03k
    txn_tablet_map_t& txn_tablet_map = _get_txn_tablet_map(transaction_id);
908
6.03k
    auto it = txn_tablet_map.find(key);
909
6.03k
    if (it == txn_tablet_map.end()) {
910
3
        VLOG_NOTICE << "could not find tablet for"
911
2
                    << " partition_id=" << partition_id << ", transaction_id=" << transaction_id;
912
3
        return;
913
3
    }
914
6.02k
    auto& load_info_map = it->second;
915
916
    // each tablet
917
47.4k
    for (auto& load_info : load_info_map) {
918
47.4k
        const TabletInfo& tablet_info = load_info.first;
919
        // must not check rowset == null here, because if rowset == null
920
        // publish version should failed
921
47.4k
        tablet_infos->emplace(tablet_info, load_info.second->rowset);
922
47.4k
        if (tablet_txn_infos != nullptr) {
923
47.1k
            tablet_txn_infos->emplace(tablet_info, load_info.second);
924
47.1k
        }
925
47.4k
    }
926
6.02k
}
927
928
58
void TxnManager::get_all_related_tablets(std::set<TabletInfo>* tablet_infos) {
929
59.4k
    for (int32_t i = 0; i < _txn_map_shard_size; i++) {
930
59.3k
        std::shared_lock txn_rdlock(_txn_map_locks[i]);
931
59.3k
        for (auto& it : _txn_tablet_maps[i]) {
932
1.12k
            for (auto& tablet_load_it : it.second) {
933
1.12k
                tablet_infos->emplace(tablet_load_it.first);
934
1.12k
            }
935
78
        }
936
59.3k
    }
937
58
}
938
939
void TxnManager::get_all_commit_tablet_txn_info_by_tablet(
940
3.19k
        const Tablet& tablet, CommitTabletTxnInfoVec* commit_tablet_txn_info_vec) {
941
3.20M
    for (int32_t i = 0; i < _txn_map_shard_size; i++) {
942
3.19M
        std::shared_lock txn_rdlock(_txn_map_locks[i]);
943
3.19M
        for (const auto& [txn_key, load_info_map] : _txn_tablet_maps[i]) {
944
1.17k
            auto tablet_load_it = load_info_map.find(tablet.get_tablet_info());
945
1.17k
            if (tablet_load_it != load_info_map.end()) {
946
130
                const auto& [_, load_info] = *tablet_load_it;
947
130
                const auto& rowset = load_info->rowset;
948
130
                const auto& delete_bitmap = load_info->delete_bitmap;
949
130
                if (!rowset || !delete_bitmap) {
950
91
                    continue;
951
91
                }
952
39
                commit_tablet_txn_info_vec->push_back({
953
39
                        .transaction_id = txn_key.second,
954
39
                        .partition_id = txn_key.first,
955
39
                        .delete_bitmap = delete_bitmap,
956
39
                        .rowset_ids = load_info->rowset_ids,
957
39
                        .partial_update_info = load_info->partial_update_info,
958
39
                });
959
39
            }
960
1.17k
        }
961
3.19M
    }
962
3.19k
}
963
964
160
void TxnManager::build_expire_txn_map(std::map<TabletInfo, std::vector<int64_t>>* expire_txn_map) {
965
160
    int64_t now = UnixSeconds();
966
    // traverse the txn map, and get all expired txns
967
164k
    for (int32_t i = 0; i < _txn_map_shard_size; i++) {
968
163k
        std::shared_lock txn_rdlock(_txn_map_locks[i]);
969
163k
        for (auto&& [txn_key, tablet_txn_infos] : _txn_tablet_maps[i]) {
970
203
            auto txn_id = txn_key.second;
971
3.12k
            for (auto&& [tablet_info, txn_info] : tablet_txn_infos) {
972
3.12k
                double diff = difftime(now, txn_info->creation_time);
973
3.12k
                if (diff < config::pending_data_expire_time_sec) {
974
3.04k
                    continue;
975
3.04k
                }
976
977
84
                (*expire_txn_map)[tablet_info].push_back(txn_id);
978
84
                if (VLOG_IS_ON(3)) {
979
0
                    VLOG_NOTICE << "find expired txn."
980
0
                                << " tablet=" << tablet_info.to_string()
981
0
                                << " transaction_id=" << txn_id << " exist_sec=" << diff;
982
0
                }
983
84
            }
984
203
        }
985
163k
    }
986
160
}
987
988
void TxnManager::get_partition_ids(const TTransactionId transaction_id,
989
26
                                   std::vector<TPartitionId>* partition_ids) {
990
26
    std::shared_lock txn_rdlock(_get_txn_map_lock(transaction_id));
991
26
    txn_partition_map_t& txn_partition_map = _get_txn_partition_map(transaction_id);
992
26
    auto it = txn_partition_map.find(transaction_id);
993
26
    if (it != txn_partition_map.end()) {
994
13
        for (int64_t partition_id : it->second) {
995
13
            partition_ids->push_back(partition_id);
996
13
        }
997
13
    }
998
26
}
999
1000
95.1k
void TxnManager::_insert_txn_partition_map_unlocked(int64_t transaction_id, int64_t partition_id) {
1001
95.1k
    txn_partition_map_t& txn_partition_map = _get_txn_partition_map(transaction_id);
1002
95.1k
    auto find = txn_partition_map.find(transaction_id);
1003
95.1k
    if (find == txn_partition_map.end()) {
1004
6.02k
        txn_partition_map[transaction_id] = std::unordered_set<int64_t>();
1005
6.02k
    }
1006
95.1k
    txn_partition_map[transaction_id].insert(partition_id);
1007
95.1k
}
1008
1009
6.02k
void TxnManager::_clear_txn_partition_map_unlocked(int64_t transaction_id, int64_t partition_id) {
1010
6.02k
    txn_partition_map_t& txn_partition_map = _get_txn_partition_map(transaction_id);
1011
6.02k
    auto it = txn_partition_map.find(transaction_id);
1012
6.02k
    if (it != txn_partition_map.end()) {
1013
6.02k
        it->second.erase(partition_id);
1014
6.02k
        if (it->second.empty()) {
1015
5.99k
            txn_partition_map.erase(it);
1016
5.99k
        }
1017
6.02k
    }
1018
6.02k
}
1019
1020
33.1k
int64_t TxnManager::get_txn_by_tablet_version(int64_t tablet_id, int64_t version) {
1021
33.1k
    char key[16];
1022
33.1k
    memcpy(key, &tablet_id, sizeof(int64_t));
1023
33.1k
    memcpy(key + sizeof(int64_t), &version, sizeof(int64_t));
1024
33.1k
    CacheKey cache_key((const char*)&key, sizeof(key));
1025
1026
33.1k
    auto* handle = _tablet_version_cache->lookup(cache_key);
1027
33.1k
    if (handle == nullptr) {
1028
33.1k
        return -1;
1029
33.1k
    }
1030
5
    int64_t res = ((CacheValue*)_tablet_version_cache->value(handle))->value;
1031
5
    _tablet_version_cache->release(handle);
1032
5
    return res;
1033
33.1k
}
1034
1035
33.1k
void TxnManager::update_tablet_version_txn(int64_t tablet_id, int64_t version, int64_t txn_id) {
1036
33.1k
    char key[16];
1037
33.1k
    memcpy(key, &tablet_id, sizeof(int64_t));
1038
33.1k
    memcpy(key + sizeof(int64_t), &version, sizeof(int64_t));
1039
33.1k
    CacheKey cache_key((const char*)&key, sizeof(key));
1040
1041
33.1k
    auto* value = new CacheValue;
1042
33.1k
    value->value = txn_id;
1043
33.1k
    auto* handle = _tablet_version_cache->insert(cache_key, value, 1, sizeof(txn_id),
1044
33.1k
                                                 CachePriority::NORMAL);
1045
33.1k
    _tablet_version_cache->release(handle);
1046
33.1k
}
1047
1048
TxnState TxnManager::get_txn_state(TPartitionId partition_id, TTransactionId transaction_id,
1049
0
                                   TTabletId tablet_id, TabletUid tablet_uid) {
1050
0
    pair<int64_t, int64_t> key(partition_id, transaction_id);
1051
0
    TabletInfo tablet_info(tablet_id, tablet_uid);
1052
1053
0
    std::shared_lock txn_rdlock(_get_txn_map_lock(transaction_id));
1054
1055
0
    auto& txn_tablet_map = _get_txn_tablet_map(transaction_id);
1056
0
    auto it = txn_tablet_map.find(key);
1057
0
    if (it == txn_tablet_map.end()) {
1058
0
        return TxnState::NOT_FOUND;
1059
0
    }
1060
1061
0
    auto& tablet_txn_info_map = it->second;
1062
0
    auto tablet_txn_info_iter = tablet_txn_info_map.find(tablet_info);
1063
0
    if (tablet_txn_info_iter == tablet_txn_info_map.end()) {
1064
0
        return TxnState::NOT_FOUND;
1065
0
    }
1066
1067
0
    const auto& txn_info = tablet_txn_info_iter->second;
1068
0
    return txn_info->state;
1069
0
}
1070
1071
} // namespace doris