/Users/deen/code/yugabyte-db/src/yb/docdb/intent.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include "yb/docdb/intent.h" |
15 | | |
16 | | #include <string> |
17 | | |
18 | | #include <glog/logging.h> |
19 | | |
20 | | #include "yb/common/row_mark.h" |
21 | | #include "yb/common/transaction.h" |
22 | | |
23 | | #include "yb/docdb/value_type.h" |
24 | | |
25 | | #include "yb/gutil/endian.h" |
26 | | |
27 | | #include "yb/util/result.h" |
28 | | #include "yb/util/status_format.h" |
29 | | |
30 | | namespace yb { |
31 | | namespace docdb { |
32 | | |
33 | 2.17M | Result<DecodedIntentKey> DecodeIntentKey(const Slice &encoded_intent_key) { |
34 | 2.17M | DecodedIntentKey result; |
35 | 2.17M | auto& intent_prefix = result.intent_prefix; |
36 | 2.17M | intent_prefix = encoded_intent_key; |
37 | | |
38 | 2.17M | size_t doc_ht_size = 0; |
39 | 2.17M | RETURN_NOT_OK(DocHybridTime::CheckAndGetEncodedSize(intent_prefix, &doc_ht_size)); |
40 | | // There should always be 3 bytes present before teh start of the doc_ht: |
41 | | // 1. ValueType::kIntentTypeSet |
42 | | // 2. the corresponding value for ValueType::kIntentTypeSet |
43 | | // 3. ValueType::kHybridTime |
44 | 2.17M | constexpr int kBytesBeforeDocHt = 3; |
45 | 2.17M | if (intent_prefix.size() < doc_ht_size + kBytesBeforeDocHt) { |
46 | 0 | return STATUS_FORMAT( |
47 | 0 | Corruption, "Intent key is too short: $0 bytes", encoded_intent_key.size()); |
48 | 0 | } |
49 | 2.17M | intent_prefix.remove_suffix(doc_ht_size + kBytesBeforeDocHt); |
50 | 2.17M | RETURN_NOT_OK(result.doc_ht.FullyDecodeFrom( |
51 | 2.17M | Slice(intent_prefix.data() + intent_prefix.size() + kBytesBeforeDocHt, doc_ht_size))); |
52 | 2.17M | auto* prefix_end = intent_prefix.end(); |
53 | | |
54 | 2.17M | if (prefix_end[2] != ValueTypeAsChar::kHybridTime) |
55 | 0 | return STATUS_FORMAT(Corruption, "Expecting hybrid time with ValueType $0, found $1", |
56 | 2.17M | ValueType::kHybridTime, static_cast<ValueType>(prefix_end[2])); |
57 | | |
58 | 2.17M | if (prefix_end[0] != ValueTypeAsChar::kIntentTypeSet) { |
59 | 0 | if (prefix_end[0] == ValueTypeAsChar::kObsoleteIntentType) { |
60 | 0 | result.intent_types = ObsoleteIntentTypeToSet(prefix_end[1]); |
61 | 0 | } else if (prefix_end[0] == ValueTypeAsChar::kObsoleteIntentTypeSet) { |
62 | 0 | result.intent_types = ObsoleteIntentTypeSetToNew(prefix_end[1]); |
63 | 0 | } else { |
64 | 0 | return STATUS_FORMAT( |
65 | 0 | Corruption, |
66 | 0 | "Expecting intent type set ($0) or intent type ($1) or obsolete intent type set ($2), " |
67 | 0 | "found $3", |
68 | 0 | ValueType::kIntentTypeSet, ValueType::kObsoleteIntentType, |
69 | 0 | ValueType::kObsoleteIntentTypeSet, static_cast<ValueType>(prefix_end[0])); |
70 | 0 | } |
71 | 2.17M | } else { |
72 | 2.17M | result.intent_types = IntentTypeSet(prefix_end[1]); |
73 | 2.17M | } |
74 | | |
75 | 2.17M | return result; |
76 | 2.17M | } |
77 | | |
78 | 6.64M | Result<TransactionId> DecodeTransactionIdFromIntentValue(Slice* intent_value) { |
79 | 6.64M | if (intent_value->empty()) { |
80 | 0 | return STATUS_FORMAT(Corruption, "Expecting intent value to start with ValueType $0, but it is " |
81 | 0 | "empty", ValueType::kTransactionId); |
82 | 6.64M | } else if (*intent_value->data() != ValueTypeAsChar::kTransactionId) { |
83 | 0 | return STATUS_FORMAT(Corruption, "Expecting intent key to start with ValueType $0, found $1", |
84 | 0 | ValueType::kTransactionId, static_cast<ValueType>(*intent_value->data())); |
85 | 6.64M | } else { |
86 | 6.64M | intent_value->consume_byte(); |
87 | 6.64M | } |
88 | 6.64M | return DecodeTransactionId(intent_value); |
89 | 6.64M | } |
90 | | |
91 | 22.5M | IntentTypeSet AllStrongIntents() { |
92 | 22.5M | return IntentTypeSet({IntentType::kStrongRead, IntentType::kStrongWrite}); |
93 | 22.5M | } |
94 | | |
95 | | IntentTypeSet GetStrongIntentTypeSet( |
96 | | IsolationLevel level, |
97 | | OperationKind operation_kind, |
98 | 6.24M | RowMarkType row_mark) { |
99 | 6.24M | if (IsValidRowMarkType(row_mark)) { |
100 | | // Mapping of postgres locking levels to DocDB intent types is described in details by the |
101 | | // following comment https://github.com/yugabyte/yugabyte-db/issues/1199#issuecomment-501041018 |
102 | 24.3k | switch (row_mark) { |
103 | 16.5k | case RowMarkType::ROW_MARK_EXCLUSIVE: |
104 | | // FOR UPDATE: strong read + strong write lock on the DocKey, |
105 | | // as if we're replacing or deleting the entire row in DocDB. |
106 | 16.5k | return IntentTypeSet({IntentType::kStrongRead, IntentType::kStrongWrite}); |
107 | 6 | case RowMarkType::ROW_MARK_NOKEYEXCLUSIVE: |
108 | | // FOR NO KEY UPDATE: strong read + weak write lock on the DocKey, as if we're reading |
109 | | // the entire row and then writing only a subset of columns in DocDB. |
110 | 6 | return IntentTypeSet({IntentType::kStrongRead, IntentType::kWeakWrite}); |
111 | 3.66k | case RowMarkType::ROW_MARK_SHARE: |
112 | | // FOR SHARE: strong read on the DocKey, as if we're reading the entire row in DocDB. |
113 | 3.66k | return IntentTypeSet({IntentType::kStrongRead}); |
114 | 4.18k | case RowMarkType::ROW_MARK_KEYSHARE: |
115 | | // FOR KEY SHARE: weak read lock on the DocKey, preventing the entire row from being |
116 | | // replaced / deleted, as if we're simply reading some of the column. |
117 | | // This is the type of locking that is used by foreign keys, so this will |
118 | | // prevent the referenced row from disappearing. The reason it does not |
119 | | // conflict with the FOR NO KEY UPDATE above is conceptually the following: |
120 | | // an operation that reads the entire row and then writes a subset of columns |
121 | | // (FOR NO KEY UPDATE) does not have to conflict with an operation that could |
122 | | // be reading a different subset of columns (FOR KEY SHARE). |
123 | 4.18k | return IntentTypeSet({IntentType::kWeakRead}); |
124 | 0 | default: |
125 | | // We shouldn't get here because other row lock types are disabled at the postgres level. |
126 | 0 | LOG(DFATAL) << "Unsupported row lock of type " << RowMarkType_Name(row_mark); |
127 | 0 | break; |
128 | 6.22M | } |
129 | 6.22M | } |
130 | | |
131 | 6.22M | switch (level) { |
132 | 39.1k | case IsolationLevel::READ_COMMITTED: |
133 | 3.12M | case IsolationLevel::SNAPSHOT_ISOLATION: |
134 | 3.12M | return IntentTypeSet({IntentType::kStrongRead, IntentType::kStrongWrite}); |
135 | 3.09M | case IsolationLevel::SERIALIZABLE_ISOLATION: |
136 | 3.09M | switch (operation_kind) { |
137 | 523k | case OperationKind::kRead: |
138 | 523k | return IntentTypeSet({IntentType::kStrongRead}); |
139 | 2.57M | case OperationKind::kWrite: |
140 | 2.57M | return IntentTypeSet({IntentType::kStrongWrite}); |
141 | 0 | } |
142 | 0 | FATAL_INVALID_ENUM_VALUE(OperationKind, operation_kind); |
143 | 0 | case IsolationLevel::NON_TRANSACTIONAL: |
144 | 0 | LOG(DFATAL) << "GetStrongIntentTypeSet invoked for non transactional isolation"; |
145 | 0 | return IntentTypeSet(); |
146 | 0 | } |
147 | 0 | FATAL_INVALID_ENUM_VALUE(IsolationLevel, level); |
148 | 0 | } |
149 | | |
150 | 22.5M | bool HasStrong(IntentTypeSet inp) { |
151 | 22.5M | return (inp & AllStrongIntents()).Any(); |
152 | 22.5M | } |
153 | | |
154 | | #define INTENT_VALUE_SCHECK(lhs, op, rhs, msg) \ |
155 | 40.0M | BOOST_PP_CAT(SCHECK_, op)(lhs, \ |
156 | 40.0M | rhs, \ |
157 | 40.0M | Corruption, \ |
158 | 40.0M | Format("Bad intent value, $0 in $1, transaction: $2", \ |
159 | 40.0M | msg, \ |
160 | 40.0M | encoded_intent_value.ToDebugHexString(), \ |
161 | 40.0M | transaction_id_slice.ToDebugHexString())) |
162 | | |
163 | | Result<DecodedIntentValue> DecodeIntentValue( |
164 | | const Slice& encoded_intent_value, const Slice* verify_transaction_id_slice, |
165 | 23.4M | bool has_strong_intent) { |
166 | 23.4M | DecodedIntentValue decoded_value; |
167 | 23.4M | auto intent_value = encoded_intent_value; |
168 | 23.4M | auto transaction_id_slice = Slice(); |
169 | | |
170 | 23.4M | if (verify_transaction_id_slice) { |
171 | 16.8M | transaction_id_slice = *verify_transaction_id_slice; |
172 | 16.8M | RETURN_NOT_OK(intent_value.consume_byte(ValueTypeAsChar::kTransactionId)); |
173 | 16.8M | INTENT_VALUE_SCHECK(intent_value.starts_with(transaction_id_slice), EQ, true, |
174 | 16.8M | "wrong transaction id"); |
175 | 16.8M | intent_value.remove_prefix(TransactionId::StaticSize()); |
176 | 6.64M | } else { |
177 | 6.64M | decoded_value.transaction_id = VERIFY_RESULT(DecodeTransactionIdFromIntentValue(&intent_value)); |
178 | 6.64M | transaction_id_slice = decoded_value.transaction_id.AsSlice(); |
179 | 6.64M | } |
180 | | |
181 | 23.4M | if (intent_value.TryConsumeByte(ValueTypeAsChar::kSubTransactionId)) { |
182 | 44.3k | decoded_value.subtransaction_id = Load<SubTransactionId, BigEndian>(intent_value.data()); |
183 | 44.3k | intent_value.remove_prefix(sizeof(SubTransactionId)); |
184 | 23.4M | } else { |
185 | 23.4M | decoded_value.subtransaction_id = kMinSubTransactionId; |
186 | 23.4M | } |
187 | | |
188 | 23.4M | if (has_strong_intent) { |
189 | 23.2M | RETURN_NOT_OK(intent_value.consume_byte(ValueTypeAsChar::kWriteId)); |
190 | 23.2M | INTENT_VALUE_SCHECK(intent_value.size(), GE, sizeof(IntraTxnWriteId), "write id expected"); |
191 | 23.2M | decoded_value.write_id = BigEndian::Load32(intent_value.data()); |
192 | 23.2M | intent_value.remove_prefix(sizeof(IntraTxnWriteId)); |
193 | 23.2M | } |
194 | | |
195 | 23.4M | decoded_value.body = intent_value; |
196 | | |
197 | 23.4M | return decoded_value; |
198 | 23.4M | } |
199 | | |
200 | 0 | IntentTypeSet ObsoleteIntentTypeToSet(uint8_t obsolete_intent_type) { |
201 | 0 | constexpr int kWeakIntentFlag = 0b000; |
202 | 0 | constexpr int kStrongIntentFlag = 0b001; |
203 | 0 | constexpr int kWriteIntentFlag = 0b010; |
204 | 0 | constexpr int kSnapshotIntentFlag = 0b100; |
205 | | |
206 | | // Actually we have only 2 types of obsolete intent types that could be present. |
207 | | // Strong and weak snapshot writes. |
208 | 0 | if (obsolete_intent_type == (kStrongIntentFlag | kWriteIntentFlag | kSnapshotIntentFlag)) { |
209 | 0 | return IntentTypeSet({IntentType::kStrongRead, IntentType::kStrongWrite}); |
210 | 0 | } else if (obsolete_intent_type == (kWeakIntentFlag | kWriteIntentFlag | kSnapshotIntentFlag)) { |
211 | 0 | return IntentTypeSet({IntentType::kWeakRead, IntentType::kWeakWrite}); |
212 | 0 | } |
213 | | |
214 | 0 | LOG(DFATAL) << "Unexpected obsolete intent type: " << static_cast<int>(obsolete_intent_type); |
215 | |
|
216 | 0 | return IntentTypeSet(); |
217 | 0 | } |
218 | | |
219 | 0 | IntentTypeSet ObsoleteIntentTypeSetToNew(uint8_t obsolete_intent_type_set) { |
220 | 0 | IntentTypeSet result; |
221 | 0 | for (size_t idx = 0; idx != 4; ++idx) { |
222 | 0 | if (obsolete_intent_type_set & (1 << idx)) { |
223 | | // We swap two bits in every index because their meanings have changed places between the |
224 | | // obsolete vs. new format. |
225 | 0 | result.Set(static_cast<IntentType>(((idx >> 1) | (idx << 1)) & 3)); |
226 | 0 | } |
227 | 0 | } |
228 | 0 | return result; |
229 | 0 | } |
230 | | |
231 | 4.86M | bool IntentValueType(char ch) { |
232 | 4.86M | return ch == ValueTypeAsChar::kIntentTypeSet || |
233 | 137k | ch == ValueTypeAsChar::kObsoleteIntentTypeSet || |
234 | 137k | ch == ValueTypeAsChar::kObsoleteIntentType; |
235 | 4.86M | } |
236 | | |
237 | | } // namespace docdb |
238 | | } // namespace yb |