YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/docdb/doc_boundary_values_extractor.cc
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
#include "yb/rocksdb/db/dbformat.h"
17
18
#include "yb/docdb/consensus_frontier.h"
19
#include "yb/docdb/doc_key.h"
20
#include "yb/docdb/value_type.h"
21
22
#include "yb/gutil/casts.h"
23
#include "yb/util/status_format.h"
24
#include "yb/util/status_log.h"
25
26
namespace yb {
27
namespace docdb {
28
29
Status GetDocHybridTime(const rocksdb::UserBoundaryValues& values, DocHybridTime* out);
30
31
Status GetPrimitiveValue(const rocksdb::UserBoundaryValues& values,
32
                         size_t index,
33
                         PrimitiveValue* out);
34
35
namespace {
36
37
constexpr rocksdb::UserBoundaryTag kDocHybridTimeTag = 1;
38
// Here we reserve some tags for future use.
39
// Because Tag is persistent.
40
constexpr rocksdb::UserBoundaryTag kRangeComponentsStart = 10;
41
42
// Wrapper for UserBoundaryValue that stores DocHybridTime.
43
class DocHybridTimeValue : public rocksdb::UserBoundaryValue {
44
 public:
45
93.1M
  explicit DocHybridTimeValue(Slice slice) {
46
93.1M
    memcpy(buffer_, slice.data(), slice.size());
47
93.1M
    encoded_ = Slice(buffer_, slice.size());
48
93.1M
  }
49
50
93.0M
  static CHECKED_STATUS Create(Slice data, rocksdb::UserBoundaryValuePtr* value) {
51
93.0M
    CHECK_NOTNULL(value);
52
93.0M
    if (data.size() > kMaxBytesPerEncodedHybridTime) {
53
0
      return STATUS_SUBSTITUTE(Corruption, "Too big encoded doc hybrid time: $0", data.size());
54
0
    }
55
56
93.0M
    *value = std::make_shared<DocHybridTimeValue>(data);
57
93.0M
    return Status::OK();
58
93.0M
  }
59
60
93.1M
  virtual ~DocHybridTimeValue() {}
61
62
638M
  rocksdb::UserBoundaryTag Tag() override {
63
638M
    return kDocHybridTimeTag;
64
638M
  }
65
66
161k
  Slice Encode() override {
67
161k
    return encoded_;
68
161k
  }
69
70
185M
  int CompareTo(const UserBoundaryValue& pre_rhs) override {
71
185M
    const auto* rhs = down_cast<const DocHybridTimeValue*>(&pre_rhs);
72
185M
    return -encoded_.compare(rhs->encoded_);
73
185M
  }
74
75
93.0M
  CHECKED_STATUS value(DocHybridTime* out) const {
76
93.0M
    CHECK_NOTNULL(out);
77
93.0M
    *out = VERIFY_RESULT(DocHybridTime::FullyDecodeFrom(encoded_));
78
0
    return Status::OK();
79
93.0M
  }
80
81
 private:
82
  char buffer_[kMaxBytesPerEncodedHybridTime];
83
  Slice encoded_;
84
};
85
86
// Wrapper for UserBoundaryValue that stores PrimitiveValue with index.
87
class PrimitiveBoundaryValue : public rocksdb::UserBoundaryValue {
88
 public:
89
58.2M
  explicit PrimitiveBoundaryValue(size_t index, Slice slice) : index_(index) {
90
58.2M
    buffer_.assign(slice.data(), slice.end());
91
58.2M
  }
92
93
58.2M
  static CHECKED_STATUS Create(size_t index, Slice data, rocksdb::UserBoundaryValuePtr* value) {
94
58.2M
    CHECK_NOTNULL(value);
95
96
58.2M
    *value = std::make_shared<PrimitiveBoundaryValue>(index, data);
97
58.2M
    return Status::OK();
98
58.2M
  }
99
100
58.2M
  virtual ~PrimitiveBoundaryValue() {}
101
102
492M
  static rocksdb::UserBoundaryTag TagForIndex(size_t index) {
103
492M
    return static_cast<uint32_t>(kRangeComponentsStart + index);
104
492M
  }
105
106
417M
  rocksdb::UserBoundaryTag Tag() override {
107
417M
    return TagForIndex(index_);
108
417M
  }
109
110
116M
  Slice Encode() override {
111
116M
    return const_cast<const PrimitiveBoundaryValue*>(this)->Encode();
112
116M
  }
113
114
290M
  Slice Encode() const {
115
290M
    return Slice(buffer_.data(), buffer_.size());
116
290M
  }
117
118
58.1M
  CHECKED_STATUS value(PrimitiveValue* out) const {
119
58.1M
    CHECK_NOTNULL(out);
120
58.1M
    PrimitiveValue result;
121
58.1M
    Slice temp = Encode();
122
58.1M
    RETURN_NOT_OK(result.DecodeFromKey(&temp));
123
58.1M
    if (!temp.empty()) {
124
0
      return STATUS_SUBSTITUTE(Corruption,
125
0
                               "Extra data left after decoding: $0, remaining: $1",
126
0
                               Encode().ToDebugString(),
127
0
                               temp.size());
128
0
    }
129
58.1M
    *out = std::move(result);
130
58.1M
    return Status::OK();
131
58.1M
  }
132
133
116M
  int CompareTo(const rocksdb::UserBoundaryValue& pre_rhs) override {
134
116M
    const auto* rhs = down_cast<const PrimitiveBoundaryValue*>(&pre_rhs);
135
116M
    return Encode().compare(rhs->Encode());
136
116M
  }
137
 private:
138
  size_t index_; // Index of corresponding range component.
139
  boost::container::small_vector<uint8_t, 128> buffer_;
140
};
141
142
class DocBoundaryValuesExtractor : public rocksdb::BoundaryValuesExtractor {
143
 public:
144
302
  virtual ~DocBoundaryValuesExtractor() {}
145
146
  Status Decode(rocksdb::UserBoundaryTag tag,
147
                Slice data,
148
101k
                rocksdb::UserBoundaryValuePtr* value) override {
149
101k
    CHECK_NOTNULL(value);
150
101k
    if (tag == kDocHybridTimeTag) {
151
23.0k
      return DocHybridTimeValue::Create(data, value);
152
23.0k
    }
153
78.3k
    
if (78.3k
tag >= kRangeComponentsStart78.3k
) {
154
78.3k
      return PrimitiveBoundaryValue::Create(tag - kRangeComponentsStart, data, value);
155
78.3k
    }
156
157
18.4E
    return STATUS_SUBSTITUTE(NotFound, "Unknown tag: $0", tag);
158
78.3k
  }
159
160
115M
  Status Extract(Slice user_key, Slice value, rocksdb::UserBoundaryValues* values) override {
161
115M
    if (docdb::IsInternalRecordKeyType(docdb::DecodeValueType(user_key))) {
162
      // Skipping internal DocDB records.
163
22.6M
      return Status::OK();
164
22.6M
    }
165
166
93.0M
    CHECK_NOTNULL(values);
167
93.0M
    boost::container::small_vector<Slice, 20> slices;
168
93.0M
    auto user_key_copy = user_key;
169
93.0M
    RETURN_NOT_OK(SubDocKey::PartiallyDecode(&user_key_copy, &slices));
170
93.0M
    size_t size = slices.size();
171
93.0M
    if (size == 0) {
172
0
      return STATUS(Corruption, "Key does not contain hybrid time", user_key.ToDebugString());
173
0
    }
174
    // Last one contains Doc Hybrid Time, so number of range is less by 1.
175
93.0M
    --size;
176
177
93.0M
    rocksdb::UserBoundaryValuePtr temp;
178
93.0M
    RETURN_NOT_OK(DocHybridTimeValue::Create(slices.back(), &temp));
179
93.0M
    values->push_back(std::move(temp));
180
181
151M
    for (size_t i = 0; i != size; 
++i58.0M
) {
182
58.0M
      RETURN_NOT_OK(PrimitiveBoundaryValue::Create(i, slices[i], &temp));
183
58.0M
      values->push_back(std::move(temp));
184
58.0M
    }
185
186
93.0M
    DCHECK(PerformSanityCheck(user_key, slices, *values));
187
188
93.0M
    return Status::OK();
189
93.0M
  }
190
191
34.2k
  rocksdb::UserFrontierPtr CreateFrontier() override {
192
34.2k
    return new docdb::ConsensusFrontier();
193
34.2k
  }
194
195
  bool PerformSanityCheck(Slice user_key,
196
                          const boost::container::small_vector_base<Slice>& slices,
197
93.1M
                          const rocksdb::UserBoundaryValues& values) {
198
93.1M
#ifndef NDEBUG
199
93.1M
    SubDocKey sub_doc_key;
200
93.1M
    CHECK_OK(sub_doc_key.FullyDecodeFrom(user_key));
201
202
93.1M
    Slice temp_slice = slices.back();
203
93.1M
    auto doc_ht = CHECK_RESULT(DocHybridTime::DecodeFrom(&temp_slice));
204
93.1M
    CHECK(temp_slice.empty());
205
93.1M
    CHECK_EQ(sub_doc_key.doc_hybrid_time(), doc_ht);
206
93.1M
    DocHybridTime doc_ht2;
207
93.1M
    CHECK_OK(GetDocHybridTime(values, &doc_ht2));
208
93.1M
    CHECK_EQ(doc_ht, doc_ht2);
209
210
93.1M
    const auto& range_group = sub_doc_key.doc_key().range_group();
211
93.1M
    CHECK_EQ(range_group.size(), slices.size() - 1);
212
213
151M
    for (size_t i = 0; i != range_group.size(); 
++i58.0M
) {
214
58.0M
      PrimitiveValue primitive_value, primitive_value2;
215
58.0M
      temp_slice = slices[i];
216
58.0M
      CHECK_OK(primitive_value.DecodeFromKey(&temp_slice));
217
58.0M
      CHECK(temp_slice.empty());
218
58.0M
      CHECK_EQ(range_group[i], primitive_value);
219
58.0M
      CHECK_OK(GetPrimitiveValue(values, i, &primitive_value2));
220
58.0M
      CHECK_EQ(range_group[i], primitive_value2);
221
58.0M
    }
222
93.1M
#endif
223
93.1M
    return true;
224
93.1M
  }
225
};
226
227
} // namespace
228
229
527k
std::shared_ptr<rocksdb::BoundaryValuesExtractor> DocBoundaryValuesExtractorInstance() {
230
527k
  static std::shared_ptr<rocksdb::BoundaryValuesExtractor> instance =
231
527k
      std::make_shared<DocBoundaryValuesExtractor>();
232
527k
  return instance;
233
527k
}
234
235
// Used in tests
236
Status GetPrimitiveValue(const rocksdb::UserBoundaryValues& values,
237
                         size_t index,
238
58.0M
                         PrimitiveValue* out) {
239
58.0M
  auto value = rocksdb::UserValueWithTag(values, PrimitiveBoundaryValue::TagForIndex(index));
240
58.0M
  if (!value) {
241
0
    return STATUS_SUBSTITUTE(NotFound, "Not found value for index $0", index);
242
0
  }
243
58.0M
  const auto* primitive_value = down_cast<PrimitiveBoundaryValue*>(value.get());
244
58.0M
  return primitive_value->value(out);
245
58.0M
}
246
247
93.0M
Status GetDocHybridTime(const rocksdb::UserBoundaryValues& values, DocHybridTime* out) {
248
93.0M
  auto value = rocksdb::UserValueWithTag(values, kDocHybridTimeTag);
249
93.0M
  if (!value) {
250
0
    return STATUS(NotFound, "Not found value for doc hybrid time");
251
0
  }
252
93.0M
  const auto* time_value = down_cast<DocHybridTimeValue*>(value.get());
253
93.0M
  return time_value->value(out);
254
93.0M
}
255
256
17.2M
rocksdb::UserBoundaryTag TagForRangeComponent(size_t index) {
257
17.2M
  return PrimitiveBoundaryValue::TagForIndex(index);
258
17.2M
}
259
260
} // namespace docdb
261
} // namespace yb