/Users/deen/code/yugabyte-db/src/yb/common/transaction.h
Line | Count | Source (jump to first uncovered line) |
1 | | // |
2 | | // Copyright (c) YugaByte, Inc. |
3 | | // |
4 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
5 | | // in compliance with the License. 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 distributed under the License |
10 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
11 | | // or implied. See the License for the specific language governing permissions and limitations |
12 | | // under the License. |
13 | | // |
14 | | // |
15 | | |
16 | | #ifndef YB_COMMON_TRANSACTION_H |
17 | | #define YB_COMMON_TRANSACTION_H |
18 | | |
19 | | #include <stdint.h> |
20 | | |
21 | | #include <functional> |
22 | | #include <iterator> |
23 | | #include <string> |
24 | | #include <type_traits> |
25 | | #include <unordered_set> |
26 | | #include <utility> |
27 | | |
28 | | #include <boost/container/small_vector.hpp> |
29 | | #include <boost/functional/hash/hash.hpp> |
30 | | #include <boost/optional/optional.hpp> |
31 | | |
32 | | #include "yb/common/common_fwd.h" |
33 | | #include "yb/common/transaction.pb.h" |
34 | | #include "yb/common/entity_ids_types.h" |
35 | | #include "yb/common/hybrid_time.h" |
36 | | |
37 | | #include "yb/gutil/template_util.h" |
38 | | |
39 | | #include "yb/util/enums.h" |
40 | | #include "yb/util/math_util.h" |
41 | | #include "yb/util/strongly_typed_uuid.h" |
42 | | #include "yb/util/uint_set.h" |
43 | | |
44 | | namespace yb { |
45 | | |
46 | | YB_STRONGLY_TYPED_UUID(TransactionId); |
47 | | using TransactionIdSet = std::unordered_set<TransactionId, TransactionIdHash>; |
48 | | using SubTransactionId = uint32_t; |
49 | | |
50 | | // By default, postgres SubTransactionId's propagated to DocDB start at 1, so we use this as a |
51 | | // minimum value on the DocDB side as well. All intents written without an explicit SubTransactionId |
52 | | // are assumed to belong to the subtransaction with this kMinSubTransactionId. |
53 | | constexpr SubTransactionId kMinSubTransactionId = 1; |
54 | | |
55 | | // Decodes transaction id from its binary representation. |
56 | | // Checks that slice contains only TransactionId. |
57 | | Result<TransactionId> FullyDecodeTransactionId(const Slice& slice); |
58 | | |
59 | | // Decodes transaction id from slice which contains binary encoding. Consumes corresponding bytes |
60 | | // from slice. |
61 | | Result<TransactionId> DecodeTransactionId(Slice* slice); |
62 | | |
63 | | using AbortedSubTransactionSet = UnsignedIntSet<SubTransactionId>; |
64 | | |
65 | | struct TransactionStatusResult { |
66 | | TransactionStatus status; |
67 | | |
68 | | // Meaning of status_time is related to status value. |
69 | | // PENDING - status_time reflects maximal guaranteed PENDING time, i.e. transaction cannot be |
70 | | // committed before this time. |
71 | | // COMMITTED - status_time is a commit time. |
72 | | // ABORTED - not used. |
73 | | HybridTime status_time; |
74 | | |
75 | | // Set of thus-far aborted subtransactions in this transaction. |
76 | | AbortedSubTransactionSet aborted_subtxn_set; |
77 | | |
78 | | TransactionStatusResult(TransactionStatus status_, HybridTime status_time_); |
79 | | |
80 | | TransactionStatusResult( |
81 | | TransactionStatus status_, HybridTime status_time_, |
82 | | AbortedSubTransactionSet aborted_subtxn_set_); |
83 | | |
84 | 624k | static TransactionStatusResult Aborted() { |
85 | 624k | return TransactionStatusResult(TransactionStatus::ABORTED, HybridTime()); |
86 | 624k | } |
87 | | |
88 | 0 | std::string ToString() const { |
89 | 0 | return YB_STRUCT_TO_STRING(status, status_time, aborted_subtxn_set); |
90 | 0 | } Unexecuted instantiation: yb::TransactionStatusResult::ToString() const Unexecuted instantiation: yb::TransactionStatusResult::ToString() const |
91 | | }; |
92 | | |
93 | 0 | inline std::ostream& operator<<(std::ostream& out, const TransactionStatusResult& result) { |
94 | 0 | return out << "{ status: " << TransactionStatus_Name(result.status) |
95 | 0 | << " status_time: " << result.status_time << " }"; |
96 | 0 | } |
97 | | |
98 | | typedef std::function<void(Result<TransactionStatusResult>)> TransactionStatusCallback; |
99 | | struct TransactionMetadata; |
100 | | |
101 | | YB_DEFINE_ENUM(TransactionLoadFlag, (kMustExist)(kCleanup)); |
102 | | typedef EnumBitSet<TransactionLoadFlag> TransactionLoadFlags; |
103 | | |
104 | | // Used by RequestStatusAt. |
105 | | struct StatusRequest { |
106 | | const TransactionId* id; |
107 | | HybridTime read_ht; |
108 | | HybridTime global_limit_ht; |
109 | | int64_t serial_no; |
110 | | const std::string* reason; |
111 | | TransactionLoadFlags flags; |
112 | | TransactionStatusCallback callback; |
113 | | |
114 | 0 | std::string ToString() const { |
115 | 0 | return Format("{ id: $0 read_ht: $1 global_limit_ht: $2 serial_no: $3 reason: $4 flags: $5}", |
116 | 0 | *id, read_ht, global_limit_ht, serial_no, *reason, flags); |
117 | 0 | } |
118 | | }; |
119 | | |
120 | | class RequestScope; |
121 | | |
122 | | struct CommitMetadata { |
123 | | HybridTime commit_ht; |
124 | | AbortedSubTransactionSet aborted_subtxn_set; |
125 | | }; |
126 | | |
127 | | class TransactionStatusManager { |
128 | | public: |
129 | 44.9k | virtual ~TransactionStatusManager() {} |
130 | | |
131 | | // If this tablet is aware that this transaction has committed, returns the commit ht for the |
132 | | // transaction. Otherwise, returns HybridTime::kInvalid. |
133 | | virtual HybridTime LocalCommitTime(const TransactionId& id) = 0; |
134 | | |
135 | | // If this tablet is aware that this transaction has committed, returns the CommitMetadata for the |
136 | | // transaction. Otherwise, returns boost::none. |
137 | | virtual boost::optional<CommitMetadata> LocalCommitData(const TransactionId& id) = 0; |
138 | | |
139 | | // Fetches status of specified transaction at specified time from transaction coordinator. |
140 | | // Callback would be invoked in any case. |
141 | | // There are the following potential cases: |
142 | | // 1. Status tablet knows transaction id and could determine it's status at this time. In this |
143 | | // case status structure is filled with transaction status with corresponding status time. |
144 | | // 2. Status tablet don't know this transaction id, in this case status structure contains |
145 | | // ABORTED status. |
146 | | // 3. Status tablet could not determine transaction status at this time. In this case callback |
147 | | // will be invoked with TryAgain result. |
148 | | // 4. Any kind of network/timeout errors would be reflected in error passed to callback. |
149 | | virtual void RequestStatusAt(const StatusRequest& request) = 0; |
150 | | |
151 | | // Prepares metadata for provided protobuf. Either trying to extract it from pb, or fetch |
152 | | // from existing metadatas. |
153 | | virtual Result<TransactionMetadata> PrepareMetadata(const TransactionMetadataPB& pb) = 0; |
154 | | |
155 | | virtual void Abort(const TransactionId& id, TransactionStatusCallback callback) = 0; |
156 | | |
157 | | virtual void Cleanup(TransactionIdSet&& set) = 0; |
158 | | |
159 | | // For each pair fills second with priority of transaction with id equals to first. |
160 | | virtual void FillPriorities( |
161 | | boost::container::small_vector_base<std::pair<TransactionId, uint64_t>>* inout) = 0; |
162 | | |
163 | | // Returns minimal running hybrid time of all running transactions. |
164 | | virtual HybridTime MinRunningHybridTime() const = 0; |
165 | | |
166 | | virtual Result<HybridTime> WaitForSafeTime(HybridTime safe_time, CoarseTimePoint deadline) = 0; |
167 | | |
168 | | virtual const TabletId& tablet_id() const = 0; |
169 | | |
170 | | private: |
171 | | friend class RequestScope; |
172 | | |
173 | | // Registers new request assigning next serial no to it. So this serial no could be used |
174 | | // to check whether one request happened before another one. |
175 | | virtual int64_t RegisterRequest() = 0; |
176 | | |
177 | | // request_id - is request id returned by RegisterRequest, that should be unregistered. |
178 | | virtual void UnregisterRequest(int64_t request_id) = 0; |
179 | | }; |
180 | | |
181 | | // Utility class that invokes RegisterRequest on creation and UnregisterRequest on deletion. |
182 | | class RequestScope { |
183 | | public: |
184 | 12.8M | RequestScope() noexcept : status_manager_(nullptr), request_id_(0) {} |
185 | | |
186 | | explicit RequestScope(TransactionStatusManager* status_manager) |
187 | 6.71M | : status_manager_(status_manager), request_id_(status_manager->RegisterRequest()) { |
188 | 6.71M | } |
189 | | |
190 | | RequestScope(RequestScope&& rhs) noexcept |
191 | 0 | : status_manager_(rhs.status_manager_), request_id_(rhs.request_id_) { |
192 | 0 | rhs.status_manager_ = nullptr; |
193 | 0 | } |
194 | | |
195 | 3.29M | void operator=(RequestScope&& rhs) { |
196 | 3.29M | Reset(); |
197 | 3.29M | status_manager_ = rhs.status_manager_; |
198 | 3.29M | request_id_ = rhs.request_id_; |
199 | 3.29M | rhs.status_manager_ = nullptr; |
200 | 3.29M | } |
201 | | |
202 | 19.5M | ~RequestScope() { |
203 | 19.5M | Reset(); |
204 | 19.5M | } |
205 | | |
206 | 1.90M | int64_t request_id() const { return request_id_; } |
207 | | |
208 | | RequestScope(const RequestScope&) = delete; |
209 | | void operator=(const RequestScope&) = delete; |
210 | | |
211 | | private: |
212 | 22.8M | void Reset() { |
213 | 22.8M | if (status_manager_) { |
214 | 6.71M | status_manager_->UnregisterRequest(request_id_); |
215 | 6.71M | status_manager_ = nullptr; |
216 | 6.71M | } |
217 | 22.8M | } |
218 | | |
219 | | TransactionStatusManager* status_manager_; |
220 | | int64_t request_id_; |
221 | | }; |
222 | | |
223 | | // Represents all metadata tracked about subtransaction state by the client in support of postgres |
224 | | // savepoints. Can be serialized and deserialized to/from SubTransactionMetadataPB. This should be |
225 | | // sent by the client on any transactional read/write requests where a savepoint has been created, |
226 | | // and finally on transaction commit. |
227 | | struct SubTransactionMetadata { |
228 | | SubTransactionId subtransaction_id = kMinSubTransactionId; |
229 | | AbortedSubTransactionSet aborted; |
230 | | |
231 | | void ToPB(SubTransactionMetadataPB* dest) const; |
232 | | |
233 | | static Result<SubTransactionMetadata> FromPB( |
234 | | const SubTransactionMetadataPB& source); |
235 | | |
236 | 0 | std::string ToString() const { |
237 | 0 | return YB_STRUCT_TO_STRING(subtransaction_id, aborted); |
238 | 0 | } Unexecuted instantiation: yb::SubTransactionMetadata::ToString() const Unexecuted instantiation: yb::SubTransactionMetadata::ToString() const |
239 | | |
240 | | // Returns true if this is the default state, i.e. default subtransaction_id. This indicates |
241 | | // whether the client has interacted with savepoints at all in the context of a session. If true, |
242 | | // the client could, for example, skip sending subtransaction-related metadata in RPCs. |
243 | | // TODO(savepoints) -- update behavior and comment to track default aborted subtransaction state |
244 | | // as well. |
245 | | bool IsDefaultState() const; |
246 | | }; |
247 | | |
248 | | std::ostream& operator<<(std::ostream& out, const SubTransactionMetadata& metadata); |
249 | | |
250 | | struct TransactionOperationContext { |
251 | | TransactionOperationContext(); |
252 | | |
253 | | TransactionOperationContext( |
254 | | const TransactionId& transaction_id_, TransactionStatusManager* txn_status_manager_); |
255 | | |
256 | | TransactionOperationContext( |
257 | | const TransactionId& transaction_id_, |
258 | | SubTransactionMetadata&& subtransaction_, |
259 | | TransactionStatusManager* txn_status_manager_); |
260 | | |
261 | | bool transactional() const; |
262 | | |
263 | 20.7M | explicit operator bool() const { |
264 | 20.7M | return txn_status_manager != nullptr; |
265 | 20.7M | } |
266 | | |
267 | | TransactionId transaction_id; |
268 | | SubTransactionMetadata subtransaction; |
269 | | TransactionStatusManager* txn_status_manager; |
270 | | }; |
271 | | |
272 | 0 | inline std::ostream& operator<<(std::ostream& out, const TransactionOperationContext& context) { |
273 | 0 | if (context.transactional()) { |
274 | 0 | out << context.transaction_id; |
275 | 0 | } else { |
276 | 0 | out << "<non transactional>"; |
277 | 0 | } |
278 | 0 | return out; |
279 | 0 | } |
280 | | |
281 | | struct TransactionMetadata { |
282 | | TransactionId transaction_id = TransactionId::Nil(); |
283 | | IsolationLevel isolation = IsolationLevel::NON_TRANSACTIONAL; |
284 | | TabletId status_tablet; |
285 | | |
286 | | // By default, a random value is picked for a newly created transaction. |
287 | | uint64_t priority = 0; |
288 | | |
289 | | // Used for snapshot isolation (as read time and for conflict resolution). |
290 | | // start_time is used only for backward compability during rolling update. |
291 | | HybridTime start_time; |
292 | | |
293 | | // Indicates whether this transaction is a local transaction or global transaction. |
294 | | TransactionLocality locality = TransactionLocality::GLOBAL; |
295 | | |
296 | | static Result<TransactionMetadata> FromPB(const TransactionMetadataPB& source); |
297 | | |
298 | | void ToPB(TransactionMetadataPB* dest) const; |
299 | | |
300 | | void TransactionIdToPB(TransactionMetadataPB* dest) const; |
301 | | |
302 | | // Fill dest with full metadata even when isolation is non transactional. |
303 | | void ForceToPB(TransactionMetadataPB* dest) const; |
304 | | |
305 | 10.2k | std::string ToString() const { |
306 | 10.2k | return Format( |
307 | 10.2k | "{ transaction_id: $0 isolation: $1 status_tablet: $2 priority: $3 start_time: $4 }", |
308 | 10.2k | transaction_id, IsolationLevel_Name(isolation), status_tablet, priority, start_time); |
309 | 10.2k | } |
310 | | }; |
311 | | |
312 | | bool operator==(const TransactionMetadata& lhs, const TransactionMetadata& rhs); |
313 | | |
314 | 0 | inline bool operator!=(const TransactionMetadata& lhs, const TransactionMetadata& rhs) { |
315 | 0 | return !(lhs == rhs); |
316 | 0 | } |
317 | | |
318 | | std::ostream& operator<<(std::ostream& out, const TransactionMetadata& metadata); |
319 | | |
320 | | MonoDelta TransactionRpcTimeout(); |
321 | | CoarseTimePoint TransactionRpcDeadline(); |
322 | | |
323 | | extern const char* kGlobalTransactionsTableName; |
324 | | extern const std::string kMetricsSnapshotsTableName; |
325 | | extern const std::string kTransactionTablePrefix; |
326 | | |
327 | | YB_DEFINE_ENUM(CleanupType, (kGraceful)(kImmediate)) |
328 | | |
329 | | } // namespace yb |
330 | | |
331 | | #endif // YB_COMMON_TRANSACTION_H |