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