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