YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/rocksdb/utilities/transactions/transaction_util.cc
Line
Count
Source (jump to first uncovered line)
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
//  This source code is licensed under the BSD-style license found in the
3
//  LICENSE file in the root directory of this source tree. An additional grant
4
//  of patent rights can be found in the PATENTS file in the same directory.
5
//
6
// The following only applies to changes made to this file as part of YugaByte development.
7
//
8
// Portions Copyright (c) YugaByte, Inc.
9
//
10
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
11
// in compliance with the License.  You may obtain a copy of the License at
12
//
13
// http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software distributed under the License
16
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17
// or implied.  See the License for the specific language governing permissions and limitations
18
// under the License.
19
//
20
21
#ifndef ROCKSDB_LITE
22
23
#ifndef __STDC_FORMAT_MACROS
24
#define __STDC_FORMAT_MACROS
25
#endif
26
27
#include "yb/rocksdb/utilities/transactions/transaction_util.h"
28
29
#include <inttypes.h>
30
#include <string>
31
#include <vector>
32
33
#include "yb/rocksdb/db/db_impl.h"
34
#include "yb/rocksdb/status.h"
35
#include "yb/rocksdb/utilities/write_batch_with_index.h"
36
#include "yb/util/string_util.h"
37
38
namespace rocksdb {
39
40
Status TransactionUtil::CheckKeyForConflicts(DBImpl* db_impl,
41
                                             ColumnFamilyHandle* column_family,
42
                                             const std::string& key,
43
                                             SequenceNumber key_seq,
44
76
                                             bool cache_only) {
45
76
  Status result;
46
47
76
  auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
48
76
  auto cfd = cfh->cfd();
49
76
  SuperVersion* sv = db_impl->GetAndRefSuperVersion(cfd);
50
51
76
  if (sv == nullptr) {
52
0
    result = STATUS(InvalidArgument, "Could not access column family " +
53
0
                                     cfh->GetName());
54
0
  }
55
56
76
  if (result.ok()) {
57
76
    SequenceNumber earliest_seq =
58
76
        db_impl->GetEarliestMemTableSequenceNumber(sv, true);
59
60
76
    result = CheckKey(db_impl, sv, earliest_seq, key_seq, key, cache_only);
61
62
76
    db_impl->ReturnAndCleanupSuperVersion(cfd, sv);
63
76
  }
64
65
76
  return result;
66
76
}
67
68
Status TransactionUtil::CheckKey(DBImpl* db_impl, SuperVersion* sv,
69
                                 SequenceNumber earliest_seq,
70
                                 SequenceNumber key_seq, const std::string& key,
71
139
                                 bool cache_only) {
72
139
  Status result;
73
139
  bool need_to_read_sst = false;
74
75
  // Since it would be too slow to check the SST files, we will only use
76
  // the memtables to check whether there have been any recent writes
77
  // to this key after it was accessed in this transaction.  But if the
78
  // Memtables do not contain a long enough history, we must fail the
79
  // transaction.
80
139
  if (earliest_seq == kMaxSequenceNumber) {
81
    // The age of this memtable is unknown.  Cannot rely on it to check
82
    // for recent writes.  This error shouldn't happen often in practice as
83
    // the Memtable should have a valid earliest sequence number except in some
84
    // corner cases (such as error cases during recovery).
85
0
    need_to_read_sst = true;
86
87
0
    if (cache_only) {
88
0
      result = STATUS(TryAgain,
89
0
                      "Transaction could not check for conflicts as the MemTable does not "
90
0
                      "countain a long enough history to check write at SequenceNumber: ",
91
0
                      ToString(key_seq));
92
0
    }
93
139
  } else if (key_seq < earliest_seq) {
94
22
    need_to_read_sst = true;
95
96
22
    if (cache_only) {
97
      // The age of this memtable is too new to use to check for recent
98
      // writes.
99
1
      char msg[1024];
100
1
      snprintf(msg, sizeof(msg),
101
1
               "Transaction could not check for conflicts for operation at "
102
1
               "SequenceNumber %" PRIu64
103
1
               " as the MemTable only contains changes newer than "
104
1
               "SequenceNumber %" PRIu64
105
1
               ".  Increasing the value of the "
106
1
               "max_write_buffer_number_to_maintain option could reduce the "
107
1
               "frequency "
108
1
               "of this error.",
109
1
               key_seq, earliest_seq);
110
1
      result = STATUS(TryAgain, msg);
111
1
    }
112
22
  }
113
114
139
  if (result.ok()) {
115
138
    SequenceNumber seq = kMaxSequenceNumber;
116
138
    bool found_record_for_key = false;
117
118
138
    Status s = db_impl->GetLatestSequenceForKey(sv, key, !need_to_read_sst,
119
138
                                                &seq, &found_record_for_key);
120
121
138
    if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
122
0
      result = s;
123
138
    } else if (found_record_for_key && (seq > key_seq)) {
124
      // Write Conflict
125
43
      result = STATUS(Busy, "");
126
43
    }
127
138
  }
128
129
139
  return result;
130
139
}
131
132
Status TransactionUtil::CheckKeysForConflicts(DBImpl* db_impl,
133
                                              const TransactionKeyMap& key_map,
134
55
                                              bool cache_only) {
135
55
  Status result;
136
137
59
  for (auto& key_map_iter : key_map) {
138
59
    uint32_t cf_id = key_map_iter.first;
139
59
    const auto& keys = key_map_iter.second;
140
141
59
    SuperVersion* sv = db_impl->GetAndRefSuperVersion(cf_id);
142
59
    if (sv == nullptr) {
143
0
      result = STATUS(InvalidArgument, "Could not access column family " +
144
0
                                       ToString(cf_id));
145
0
      break;
146
0
    }
147
148
59
    SequenceNumber earliest_seq =
149
59
        db_impl->GetEarliestMemTableSequenceNumber(sv, true);
150
151
    // For each of the keys in this transaction, check to see if someone has
152
    // written to this key since the start of the transaction.
153
63
    for (const auto& key_iter : keys) {
154
63
      const auto& key = key_iter.first;
155
63
      const SequenceNumber key_seq = key_iter.second.seq;
156
157
63
      result = CheckKey(db_impl, sv, earliest_seq, key_seq, key, cache_only);
158
159
63
      if (!result.ok()) {
160
19
        break;
161
19
      }
162
63
    }
163
164
59
    db_impl->ReturnAndCleanupSuperVersion(cf_id, sv);
165
166
59
    if (!result.ok()) {
167
19
      break;
168
19
    }
169
59
  }
170
171
55
  return result;
172
55
}
173
174
175
}  // namespace rocksdb
176
177
#endif  // ROCKSDB_LITE