YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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
6.14M
Result<DecodedIntentKey> DecodeIntentKey(const Slice &encoded_intent_key) {
34
6.14M
  DecodedIntentKey result;
35
6.14M
  auto& intent_prefix = result.intent_prefix;
36
6.14M
  intent_prefix = encoded_intent_key;
37
38
6.14M
  size_t doc_ht_size = 0;
39
6.14M
  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
6.14M
  constexpr int kBytesBeforeDocHt = 3;
45
6.14M
  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
6.14M
  intent_prefix.remove_suffix(doc_ht_size + kBytesBeforeDocHt);
50
6.14M
  result.doc_ht = VERIFY_RESULT(DocHybridTime::FullyDecodeFrom(
51
0
      Slice(intent_prefix.data() + intent_prefix.size() + kBytesBeforeDocHt, doc_ht_size)));
52
0
  auto* prefix_end = intent_prefix.end();
53
54
6.14M
  if (prefix_end[2] != ValueTypeAsChar::kHybridTime)
55
0
    return STATUS_FORMAT(Corruption, "Expecting hybrid time with ValueType $0, found $1",
56
6.14M
        ValueType::kHybridTime, static_cast<ValueType>(prefix_end[2]));
57
58
6.14M
  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
6.14M
  } else {
72
6.14M
    result.intent_types = IntentTypeSet(prefix_end[1]);
73
6.14M
  }
74
75
6.14M
  return result;
76
6.14M
}
77
78
16.0M
Result<TransactionId> DecodeTransactionIdFromIntentValue(Slice* intent_value) {
79
16.0M
  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
16.0M
  } 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
16.0M
  } else {
86
16.0M
    intent_value->consume_byte();
87
16.0M
  }
88
16.0M
  return DecodeTransactionId(intent_value);
89
16.0M
}
90
91
62.9M
IntentTypeSet AllStrongIntents() {
92
62.9M
  return IntentTypeSet({IntentType::kStrongRead, IntentType::kStrongWrite});
93
62.9M
}
94
95
IntentTypeSet GetStrongIntentTypeSet(
96
    IsolationLevel level,
97
    OperationKind operation_kind,
98
21.8M
    RowMarkType row_mark) {
99
21.8M
  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
45.5k
    switch (row_mark) {
103
28.1k
      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
28.1k
        return IntentTypeSet({IntentType::kStrongRead, IntentType::kStrongWrite});
107
791
      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
791
        return IntentTypeSet({IntentType::kStrongRead, IntentType::kWeakWrite});
111
5.16k
      case RowMarkType::ROW_MARK_SHARE:
112
        // FOR SHARE: strong read on the DocKey, as if we're reading the entire row in DocDB.
113
5.16k
        return IntentTypeSet({IntentType::kStrongRead});
114
11.3k
      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
11.3k
        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
45.5k
    }
129
45.5k
  }
130
131
21.8M
  switch (level) {
132
27.6k
    case IsolationLevel::READ_COMMITTED:
133
8.93M
    case IsolationLevel::SNAPSHOT_ISOLATION:
134
8.93M
      return IntentTypeSet({IntentType::kStrongRead, IntentType::kStrongWrite});
135
12.9M
    case IsolationLevel::SERIALIZABLE_ISOLATION:
136
12.9M
      switch (operation_kind) {
137
1.20M
        case OperationKind::kRead:
138
1.20M
          return IntentTypeSet({IntentType::kStrongRead});
139
11.7M
        case OperationKind::kWrite:
140
11.7M
          return IntentTypeSet({IntentType::kStrongWrite});
141
12.9M
      }
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
21.8M
  }
147
0
  FATAL_INVALID_ENUM_VALUE(IsolationLevel, level);
148
0
}
149
150
62.9M
bool HasStrong(IntentTypeSet inp) {
151
62.9M
  return (inp & AllStrongIntents()).Any();
152
62.9M
}
153
154
#define INTENT_VALUE_SCHECK(lhs, op, rhs, msg) \
155
121M
  BOOST_PP_CAT(SCHECK_, op)(lhs, \
156
121M
                            rhs, \
157
121M
                            Corruption, \
158
121M
                            Format("Bad intent value, $0 in $1, transaction: $2", \
159
121M
                                   msg, \
160
121M
                                   encoded_intent_value.ToDebugHexString(), \
161
121M
                                   transaction_id_slice.ToDebugHexString()))
162
163
Result<DecodedIntentValue> DecodeIntentValue(
164
    const Slice& encoded_intent_value, const Slice* verify_transaction_id_slice,
165
68.7M
    bool has_strong_intent) {
166
68.7M
  DecodedIntentValue decoded_value;
167
68.7M
  auto intent_value = encoded_intent_value;
168
68.7M
  auto transaction_id_slice = Slice();
169
170
68.7M
  if (verify_transaction_id_slice) {
171
52.7M
    transaction_id_slice = *verify_transaction_id_slice;
172
52.7M
    RETURN_NOT_OK(intent_value.consume_byte(ValueTypeAsChar::kTransactionId));
173
52.7M
    INTENT_VALUE_SCHECK(intent_value.starts_with(transaction_id_slice), EQ, true,
174
52.7M
        "wrong transaction id");
175
52.7M
    intent_value.remove_prefix(TransactionId::StaticSize());
176
52.7M
  } else {
177
16.0M
    decoded_value.transaction_id = VERIFY_RESULT(DecodeTransactionIdFromIntentValue(&intent_value));
178
0
    transaction_id_slice = decoded_value.transaction_id.AsSlice();
179
16.0M
  }
180
181
68.7M
  if (intent_value.TryConsumeByte(ValueTypeAsChar::kSubTransactionId)) {
182
54.1k
    decoded_value.subtransaction_id = Load<SubTransactionId, BigEndian>(intent_value.data());
183
54.1k
    intent_value.remove_prefix(sizeof(SubTransactionId));
184
68.7M
  } else {
185
68.7M
    decoded_value.subtransaction_id = kMinSubTransactionId;
186
68.7M
  }
187
188
68.7M
  if (has_strong_intent) {
189
68.4M
    RETURN_NOT_OK(intent_value.consume_byte(ValueTypeAsChar::kWriteId));
190
68.4M
    INTENT_VALUE_SCHECK(intent_value.size(), GE, sizeof(IntraTxnWriteId), "write id expected");
191
68.4M
    decoded_value.write_id = BigEndian::Load32(intent_value.data());
192
68.4M
    intent_value.remove_prefix(sizeof(IntraTxnWriteId));
193
68.4M
  }
194
195
68.7M
  decoded_value.body = intent_value;
196
197
68.7M
  return decoded_value;
198
68.7M
}
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
11.0M
bool IntentValueType(char ch) {
232
11.0M
  return ch == ValueTypeAsChar::kIntentTypeSet ||
233
11.0M
         
ch == ValueTypeAsChar::kObsoleteIntentTypeSet249k
||
234
11.0M
         
ch == ValueTypeAsChar::kObsoleteIntentType249k
;
235
11.0M
}
236
237
}  // namespace docdb
238
}  // namespace yb