1 /* 2 * Copyright (c) 2021 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 #ifndef MULTI_VER_VACUUM_H 17 #define MULTI_VER_VACUUM_H 18 19 #ifndef OMIT_MULTI_VER 20 #include <map> 21 #include <list> 22 #include <mutex> 23 #include <atomic> 24 #include <string> 25 #include <vector> 26 #include <cstdint> 27 #include <condition_variable> 28 #include "macro_utils.h" 29 #include "multi_ver_vacuum_executor.h" 30 31 namespace DistributedDB { 32 enum class VacuumTaskStatus { 33 RUN_WAIT, 34 RUN_NING, 35 PAUSE_WAIT, 36 PAUSE_DONE, 37 ABORT_WAIT, 38 ABORT_DONE, 39 FINISH, 40 }; 41 42 struct VacuumTaskContext { 43 VacuumTaskStatus status = VacuumTaskStatus::RUN_WAIT; 44 bool launchErrorHappen = false; 45 bool autoRelaunchOnce = false; 46 bool immediatelyRelaunchable = true; 47 uint64_t runWaitOrder = 0; 48 uint64_t pauseNeedCount = 0; 49 MultiVerVacuumExecutor *databaseHandle = nullptr; 50 // Information to conduct the vacuum task and record the progress. 51 // When in RUN_NING, PAUSE_WAIT, ABORT_WAIT status, no other thread except the only one background task thread 52 // will access and change these field, so there is no concurrency risk accessing and changing it without a lock. 53 std::list<MultiVerCommitInfo> leftBranchCommits; 54 std::list<MultiVerCommitInfo> rightBranchCommits; 55 std::list<MultiVerRecordInfo> vacuumNeedRecords; 56 std::list<MultiVerRecordInfo> shadowRecords; 57 bool isTransactionStarted = false; 58 }; 59 60 // Pause and Continue should be call in pair for the same database. If Pause called more than Continue, then the task 61 // will stay in "inactive status". If Continue called more than Pause, then the excessive Continue will be neglected. 62 // It expected that every Pause following by a Continue sooner or later before Abort is called. 63 class MultiVerVacuum { 64 public: 65 // Default is enable, should be called at very first to change it, take effect only on instances created after it. 66 static void Enable(bool isEnable); 67 68 DISABLE_COPY_ASSIGN_MOVE(MultiVerVacuum); 69 70 // Call it when database firstly open 71 int Launch(const std::string &dbIdentifier, MultiVerVacuumExecutor *dbHandle); 72 73 // Call it before database do write operation, it may block for a while if task is running 74 // It is guaranteed that no write transaction of this database will be used by vacuum after pause return 75 int Pause(const std::string &dbIdentifier); 76 77 // Call it after database do write operation, and write transaction of this database may be used by vacuum. 78 // If autoRelaunchOnce true, the task will relaunch itself when finish, set false will not override previous true 79 int Continue(const std::string &dbIdentifier, bool autoRelaunchOnce); 80 81 // Call it when database is about to close, it may block for a while if task is running 82 int Abort(const std::string &dbIdentifier); 83 84 // Call it when observer_callback done or release an snapshot, to relaunch with some newer vacuumable commits 85 int AutoRelaunchOnce(const std::string &dbIdentifier); 86 87 int QueryStatus(const std::string &dbIdentifier, VacuumTaskStatus &outStatus) const; 88 89 MultiVerVacuum() = default; 90 ~MultiVerVacuum(); 91 private: 92 void VacuumTaskExecutor(); 93 void ExecuteSpecificVacuumTask(VacuumTaskContext &inTask); 94 95 int DealWithLeftBranchCommit(VacuumTaskContext &inTask); 96 int DealWithLeftBranchVacuumNeedRecord(VacuumTaskContext &inTask); 97 int DealWithLeftBranchShadowRecord(VacuumTaskContext &inTask); 98 int DealWithRightBranchCommit(VacuumTaskContext &inTask); 99 int DealWithRightBranchVacuumNeedRecord(VacuumTaskContext &inTask); 100 101 // Reducing duplicated code by merging similar code procedure of "DealLeftCommit" and "DealRightCommit" 102 int DoDealCommitOfLeftOrRight(VacuumTaskContext &inTask, std::list<MultiVerCommitInfo> &commitList, bool isLeft); 103 104 // Reducing duplicated code by merging similar code procedure of "DealLeftShadow" and "DealRightVacuumNeed" 105 int DoDeleteRecordOfLeftShadowOrRightVacuumNeed(VacuumTaskContext &inTask, 106 std::list<MultiVerRecordInfo> &recordList); 107 108 // Only for reducing duplicated code 109 void DoRollBackAndFinish(VacuumTaskContext &inTask); 110 int DoCommitAndQuitIfWaitStatusObserved(VacuumTaskContext &inTask); // Return E_OK continue otherwise quit 111 112 // Call this immediately before changing the database 113 int StartTransactionIfNotYet(VacuumTaskContext &inTask); 114 115 // Call this immediately before normally quit 116 int CommitTransactionIfNeed(VacuumTaskContext &inTask); 117 118 // Call this immediately before abnormally quit, return void since already in abnormal. 119 void RollBackTransactionIfNeed(VacuumTaskContext &inTask); 120 121 // All these following functions should be protected by the vacuumTaskMutex_ when called 122 void FinishVaccumTask(VacuumTaskContext &inTask); 123 void RelaunchVacuumTask(VacuumTaskContext &inTask); 124 void AbortVacuumTask(VacuumTaskContext &inTask); 125 void ResetNodeAndRecordContextInfo(VacuumTaskContext &inTask); 126 int SearchVacuumTaskToExecute(std::string &outDbIdentifier); 127 void ActivateBackgroundVacuumTaskExecution(); 128 void IncPauseNeedCount(VacuumTaskContext &inTask); 129 void DecPauseNeedCount(VacuumTaskContext &inTask); 130 bool IsPauseNotNeed(VacuumTaskContext &inTask); 131 132 static std::atomic<bool> enabled_; 133 134 mutable std::mutex vacuumTaskMutex_; 135 std::condition_variable vacuumTaskCv_; 136 uint64_t incRunWaitOrder_ = 0; 137 std::map<std::string, VacuumTaskContext> dbMapVacuumTask_; 138 139 // the search of available vacuumtask, the change of isBackgroundVacuumTaskInExecution_, and the activation of 140 // background execution, should all be protected by vacuumTaskMutex_, In order to avoid malfunction caused by 141 // concurrency situation which is described below: 142 // 1:Background search vacuumtask return none so decided to exit. 143 // 2:Foreground make vacuumtask available. 144 // 3:Foreground check isBackgroundVacuumTaskInExecution_ true so decided to nothing. 145 // 4:Background set isBackgroundVacuumTaskInExecution_ to false and exit. 146 // In this situation, no background execution running with available vacuumtask needs to be done. 147 bool isBackgroundVacuumTaskInExecution_ = false; 148 }; 149 } // namespace DistributedDB 150 151 #endif // MULTI_VER_VACUUM_H 152 #endif 153