1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "clouddisk_rdb_transaction.h"
17 #include "utils_log.h"
18
19 namespace OHOS {
20 namespace FileManagement {
21 namespace CloudDisk {
22 using namespace std;
23
24 constexpr int32_t E_HAS_DB_ERROR = -222;
25 constexpr int32_t E_OK = 0;
26 constexpr int32_t CREATE_TRANSATION_MAX_TRY_TIMES = 30;
27 constexpr int32_t COMMIT_MAX_TRY_TIMES = 3;
28 constexpr int32_t TRANSACTION_WAIT_INTERVAL = 50;
29
TransactionOperations(const shared_ptr<OHOS::NativeRdb::RdbStore> & rdbStore)30 TransactionOperations::TransactionOperations(
31 const shared_ptr<OHOS::NativeRdb::RdbStore> &rdbStore) : rdbStore_(rdbStore) {}
32
~TransactionOperations()33 TransactionOperations::~TransactionOperations()
34 {
35 if (isStart && !isFinish) {
36 LOGI("TransactionOperations::RollBack");
37 TransactionRollback();
38 }
39 }
40
41 template <typename Func>
RetryTransaction(Func func,int maxAttempts,int waitTimeMs)42 int32_t RetryTransaction(Func func, int maxAttempts, int waitTimeMs)
43 {
44 int curTryTime = 0;
45 while (curTryTime < maxAttempts) {
46 int32_t errCode = func();
47 if (errCode != NativeRdb::E_OK) {
48 curTryTime++;
49 LOGE("try %{public}d times...", curTryTime);
50 this_thread::sleep_for(chrono::milliseconds(waitTimeMs));
51 continue;
52 }
53 return E_OK;
54 }
55 LOGE("Transaction operation still failed after try %{public}d times, abort.", maxAttempts);
56 return E_HAS_DB_ERROR;
57 }
58
Start(NativeRdb::Transaction::TransactionType type)59 std::pair<int32_t, std::shared_ptr<NativeRdb::Transaction>> TransactionOperations::Start(
60 NativeRdb::Transaction::TransactionType type)
61 {
62 if (isStart || isFinish) {
63 LOGE("start transaction failed, transaction has been started.");
64 return make_pair(E_HAS_DB_ERROR, nullptr);
65 }
66
67 LOGI("TransactionOperations::Start");
68 int32_t errCode = BeginTransaction(type);
69 if (errCode == E_OK) {
70 isStart = true;
71 }
72 return make_pair(errCode, transaction_);
73 }
74
Finish()75 void TransactionOperations::Finish()
76 {
77 if (!isStart || isFinish) {
78 return;
79 }
80
81 if (!isFinish) {
82 LOGI("TransactionOperations::Finish");
83 int32_t ret = TransactionCommit();
84 if (ret == E_OK) {
85 isFinish = true;
86 isStart = false;
87 transaction_.reset();
88 return;
89 }
90 LOGE("TransactionCommit failed, errCode=%{public}d, try to rollback", ret);
91 ret = TransactionRollback();
92 if (ret != E_OK) {
93 LOGE("TransactionRollback failed, errCode=%{public}d", ret);
94 }
95 }
96 }
97
BeginTransaction(NativeRdb::Transaction::TransactionType type)98 int32_t TransactionOperations::BeginTransaction(NativeRdb::Transaction::TransactionType type)
99 {
100 if (rdbStore_ == nullptr) {
101 LOGE("Pointer rdbStore_ is nullptr. Maybe it didn't init successfully.");
102 return E_HAS_DB_ERROR;
103 }
104 LOGI("Start transaction");
105 int curTryTime = 0;
106 while (curTryTime < CREATE_TRANSATION_MAX_TRY_TIMES) {
107 int32_t errCode = E_OK;
108 std::tie(errCode, transaction_) = rdbStore_->CreateTransaction(type);
109 if (errCode == NativeRdb::E_SQLITE_LOCKED || errCode == NativeRdb::E_DATABASE_BUSY ||
110 errCode == NativeRdb::E_SQLITE_BUSY) {
111 curTryTime++;
112 LOGE("Sqlite database file is locked! try %{public}d times...", curTryTime);
113 this_thread::sleep_for(chrono::milliseconds(TRANSACTION_WAIT_INTERVAL));
114 continue;
115 } else if (errCode != NativeRdb::E_OK) {
116 LOGE("Start Transaction failed, errCode=%{public}d", errCode);
117 return errCode;
118 }
119 if (transaction_ != nullptr) {
120 isStart = true;
121 return E_OK;
122 }
123 }
124 LOGE("RdbStore is still in transaction after try %{public}d times, abort.", CREATE_TRANSATION_MAX_TRY_TIMES);
125 return E_HAS_DB_ERROR;
126 }
127
TransactionCommit()128 int32_t TransactionOperations::TransactionCommit()
129 {
130 if (transaction_ == nullptr) {
131 return E_HAS_DB_ERROR;
132 }
133 LOGI("Try commit transaction");
134
135 return RetryTransaction(
136 [this]() {
137 return transaction_->Commit();
138 }, COMMIT_MAX_TRY_TIMES, TRANSACTION_WAIT_INTERVAL);
139 }
140
TransactionRollback()141 int32_t TransactionOperations::TransactionRollback()
142 {
143 if (transaction_ == nullptr) {
144 return E_HAS_DB_ERROR;
145 }
146 LOGI("Try rollback transaction");
147
148 return RetryTransaction(
149 [this]() {
150 return transaction_->Rollback();
151 }, COMMIT_MAX_TRY_TIMES, TRANSACTION_WAIT_INTERVAL);
152 }
153 } // namespace CloudDisk
154 } // namespace FileManagement
155 } // namespace OHOS