Coverage Report

Created: 2026-11-12 08:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/db/version_edit.cc
Line
Count
Source
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
//  This source code is licensed under both the GPLv2 (found in the
3
//  COPYING file in the root directory) and Apache 2.0 License
4
//  (found in the LICENSE.Apache file in the root directory).
5
//
6
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7
// Use of this source code is governed by a BSD-style license that can be
8
// found in the LICENSE file. See the AUTHORS file for names of contributors.
9
10
#include "db/version_edit.h"
11
12
#include "db/blob/blob_index.h"
13
#include "db/version_set.h"
14
#include "db/wide/wide_column_serialization.h"
15
#include "logging/event_logger.h"
16
#include "rocksdb/slice.h"
17
#include "table/unique_id_impl.h"
18
#include "test_util/sync_point.h"
19
#include "util/coding.h"
20
#include "util/string_util.h"
21
22
namespace ROCKSDB_NAMESPACE {
23
24
namespace {
25
// For DebugString() / DebugJSON(), e.g. "ingestion+moved+raw:0x1103" (the raw
26
// value shows reserved bits that aren't decoded).
27
0
std::string DataFileProvenanceToString(uint32_t encoded) {
28
0
  const DataFileProvenance info = DataFileProvenance::DecodeFrom(encoded);
29
0
  std::string r;
30
0
  switch (info.origin) {
31
0
    case DataFileProvenance::Origin::kUnknown:
32
0
      r = "unknown_origin";
33
0
      break;
34
0
    case DataFileProvenance::Origin::kFlush:
35
0
      r = "flush";
36
0
      break;
37
0
    case DataFileProvenance::Origin::kCompaction:
38
0
      r = "compaction";
39
0
      break;
40
0
    case DataFileProvenance::Origin::kIngestion:
41
0
      r = "ingestion";
42
0
      break;
43
0
    case DataFileProvenance::Origin::kImport:
44
0
      r = "import";
45
0
      break;
46
0
  }
47
0
  if (info.moved_between_levels) {
48
0
    r += "+moved";
49
0
  }
50
0
  if (info.temperature_changed_by_copy) {
51
0
    r += "+temperature_copied";
52
0
  }
53
0
  if (info.temperature_updated_from_file_system) {
54
0
    r += "+temperature_updated";
55
0
  }
56
0
  if (info.recorded_by_repair) {
57
0
    r += "+repaired";
58
0
  }
59
0
  char raw[24];
60
0
  snprintf(raw, sizeof(raw), "+raw:0x%x", encoded);
61
0
  r += raw;
62
0
  return r;
63
0
}
64
}  // anonymous namespace
65
66
0
uint32_t DataFileProvenance::Encode() const {
67
0
  uint32_t encoded = DataFileProvenanceForOrigin(origin);
68
0
  if (moved_between_levels) {
69
0
    encoded |= kProvenanceMovedBetweenLevels;
70
0
  }
71
0
  if (temperature_changed_by_copy) {
72
0
    encoded |= kProvenanceTemperatureChangedByCopy;
73
0
  }
74
0
  if (temperature_updated_from_file_system) {
75
0
    encoded |= kProvenanceTemperatureUpdatedFromFileSystem;
76
0
  }
77
0
  if (recorded_by_repair) {
78
0
    encoded |= kProvenanceRecordedByRepair;
79
0
  }
80
0
  return encoded;
81
0
}
82
83
0
DataFileProvenance DataFileProvenance::DecodeFrom(uint32_t encoded) {
84
0
  DataFileProvenance info;
85
0
  const uint32_t origin_value = encoded & kProvenanceOriginMask;
86
0
  if (origin_value <= DataFileProvenanceForOrigin(Origin::kImport)) {
87
0
    info.origin = static_cast<Origin>(origin_value);
88
0
  }
89
0
  info.moved_between_levels = (encoded & kProvenanceMovedBetweenLevels) != 0;
90
0
  info.temperature_changed_by_copy =
91
0
      (encoded & kProvenanceTemperatureChangedByCopy) != 0;
92
0
  info.temperature_updated_from_file_system =
93
0
      (encoded & kProvenanceTemperatureUpdatedFromFileSystem) != 0;
94
0
  info.recorded_by_repair = (encoded & kProvenanceRecordedByRepair) != 0;
95
0
  return info;
96
0
}
97
98
// After copying, we rely on the caller to ensure no double releases. Fragile,
99
// but keeps copying cheap.
100
PinnedTableReader& PinnedTableReader::operator=(
101
854k
    const PinnedTableReader& other) {
102
854k
  TableReader* r = other.reader_.load(std::memory_order_acquire);
103
  // Only read handle_ when reader_ is non-null. Pin() writes handle_ before
104
  // reader_ (with release), so a non-null reader_ guarantees handle_ is stable.
105
  // If reader_ is null, Pin() may be in progress -- avoid reading handle_.
106
854k
  handle_ = (r != nullptr) ? other.handle_ : nullptr;
107
854k
  reader_.store(r, std::memory_order_release);
108
854k
  return *this;
109
854k
}
110
111
93.9k
Cache::Handle* PinnedTableReader::GetCacheHandle() const {
112
93.9k
  (void)reader_.load(std::memory_order_acquire);
113
93.9k
  return handle_;
114
93.9k
}
115
116
93.6k
void PinnedTableReader::Pin(Cache::Handle* handle, TableReader* reader) {
117
93.6k
  assert(handle != nullptr);
118
93.6k
  assert(reader != nullptr);
119
93.6k
  handle_ = handle;
120
93.6k
  reader_.store(reader, std::memory_order_release);
121
93.6k
}
122
123
0
void PinnedTableReader::Release(Cache* cache) {
124
0
  (void)reader_.load(std::memory_order_acquire);
125
0
  if (handle_ != nullptr) {
126
0
    cache->Release(handle_);
127
0
    handle_ = nullptr;
128
0
    reader_.store(nullptr, std::memory_order_relaxed);
129
0
  }
130
0
}
131
132
861k
uint64_t PackFileNumberAndPathId(uint64_t number, uint64_t path_id) {
133
861k
  assert(number <= kFileNumberMask);
134
861k
  return number | (path_id * (kFileNumberMask + 1));
135
861k
}
136
137
Status FileMetaData::UpdateBoundaries(const Slice& key, const Slice& value,
138
                                      SequenceNumber seqno,
139
56.1k
                                      ValueType value_type) {
140
  // Helper: update oldest_blob_file_number from a single BlobIndex.
141
56.1k
  auto update_oldest_blob = [&](const BlobIndex& blob_index) -> Status {
142
0
    if (!blob_index.IsInlined() && !blob_index.HasTTL()) {
143
      // INTEGRITY TRIPWIRE -- do not weaken. This is generic file metadata on
144
      // the table-build output path (flush / compaction / recovery, via
145
      // BuildTable and CompactionOutputs), which must only ever see external
146
      // blob-file references. file_number 0 is kInvalidBlobFileNumber, which is
147
      // deliberately also the same-file ("embedded") sentinel
148
      // kCurrentFileBlobIndexFileNumber (see blob_constants.h). Reaching here
149
      // with 0 means one of:
150
      //   * a same-file/embedded reference leaked into the output stream
151
      //     unresolved -- e.g. EmbeddedBlobResolvingIterator failed to resolve
152
      //     and fell back to the raw value on a blob-region read error
153
      //     (T277566778); or
154
      //   * an uninitialized/corrupt BlobIndex (default file number never set).
155
      // Rejecting it flags the bug closer to its root cause. Do NOT relax this
156
      // to skip same-file refs (e.g. via BlobIndex::IsSameFile()): that would
157
      // mask real corruption AND could persist unresolvable same-file
158
      // references into ordinary (non-embedded) SSTs, turning a transient error
159
      // into permanent data loss.
160
0
      if (blob_index.file_number() == kInvalidBlobFileNumber) {
161
0
        return Status::Corruption("Invalid blob file number");
162
0
      }
163
164
0
      if (oldest_blob_file_number == kInvalidBlobFileNumber ||
165
0
          oldest_blob_file_number > blob_index.file_number()) {
166
0
        oldest_blob_file_number = blob_index.file_number();
167
0
      }
168
0
    }
169
0
    return Status::OK();
170
0
  };
171
172
56.1k
  if (value_type == kTypeBlobIndex) {
173
0
    BlobIndex blob_index;
174
0
    if (Status s = blob_index.DecodeFrom(value); !s.ok()) {
175
0
      return s;
176
0
    }
177
178
0
    if (Status s = update_oldest_blob(blob_index); !s.ok()) {
179
0
      return s;
180
0
    }
181
56.1k
  } else if (value_type == kTypeWideColumnEntity) {
182
0
    if (Status s = WideColumnSerialization::ForEachBlobFileNumber(
183
0
            value, update_oldest_blob);
184
0
        !s.ok()) {
185
0
      return s;
186
0
    }
187
0
  }
188
189
56.1k
  if (smallest.size() == 0) {
190
18.7k
    smallest.DecodeFrom(key);
191
18.7k
  }
192
56.1k
  largest.DecodeFrom(key);
193
56.1k
  fd.smallest_seqno = std::min(fd.smallest_seqno, seqno);
194
56.1k
  fd.largest_seqno = std::max(fd.largest_seqno, seqno);
195
196
56.1k
  return Status::OK();
197
56.1k
}
198
199
393k
void VersionEdit::Clear() { *this = VersionEdit(); }
200
201
void VersionEdit::AddExistingFile(int level, const FileMetaData& f,
202
74.8k
                                  uint32_t provenance_flags_to_set) {
203
74.8k
  assert((provenance_flags_to_set & kProvenanceOriginMask) == 0);
204
74.8k
  AddFile(level, f.fd.GetNumber(), f.fd.GetPathId(), f.fd.GetFileSize(),
205
74.8k
          f.smallest, f.largest, f.fd.smallest_seqno, f.fd.largest_seqno,
206
74.8k
          f.marked_for_compaction, f.temperature, f.oldest_blob_file_number,
207
74.8k
          f.oldest_ancestor_time, f.file_creation_time, f.epoch_number,
208
74.8k
          f.file_checksum, f.file_checksum_func_name, f.unique_id,
209
74.8k
          f.compensated_range_deletion_size, f.tail_size,
210
74.8k
          f.user_defined_timestamps_persisted, f.min_timestamp, f.max_timestamp,
211
74.8k
          f.file_open_metadata);
212
74.8k
  FileMetaData& added = new_files_.back().second;
213
74.8k
  added.db_ownership_start_time = f.db_ownership_start_time;
214
74.8k
  added.data_file_provenance = f.data_file_provenance | provenance_flags_to_set;
215
74.8k
}
216
217
bool VersionEdit::ShouldEmitPerColumnFamilyRecoveryEdit(
218
0
    uint64_t current_log_number) const {
219
0
  return (HasLogNumber() && GetLogNumber() > current_log_number) ||
220
0
         NumEntries() > 0 || HasComparatorName() || HasPrevLogNumber() ||
221
0
         HasNextFile() || HasMaxColumnFamily() || HasMinLogNumberToKeep() ||
222
0
         HasLastSequence() || !GetCompactCursors().empty() || HasDbId() ||
223
0
         IsColumnFamilyManipulation() || IsInAtomicGroup() ||
224
0
         HasFullHistoryTsLow() || HasPersistUserDefinedTimestamps() ||
225
0
         HasSubcompactionProgress() || HasCommittedOptionsFileNumber() ||
226
0
         HasDroppedBlobFiles();
227
0
}
228
229
bool VersionEdit::EncodeTo(std::string* dst,
230
474k
                           std::optional<size_t> ts_sz) const {
231
474k
  assert(!IsNoManifestWriteDummy());
232
474k
  if (has_db_id_) {
233
53.0k
    PutVarint32(dst, kDbId);
234
53.0k
    PutLengthPrefixedSlice(dst, db_id_);
235
53.0k
  }
236
474k
  if (has_comparator_) {
237
76.2k
    assert(has_persist_user_defined_timestamps_);
238
76.2k
    PutVarint32(dst, kComparator);
239
76.2k
    PutLengthPrefixedSlice(dst, comparator_);
240
76.2k
  }
241
474k
  if (has_log_number_) {
242
138k
    PutVarint32Varint64(dst, kLogNumber, log_number_);
243
138k
  }
244
474k
  if (has_prev_log_number_) {
245
179k
    PutVarint32Varint64(dst, kPrevLogNumber, prev_log_number_);
246
179k
  }
247
474k
  if (has_next_file_number_) {
248
219k
    PutVarint32Varint64(dst, kNextFileNumber, next_file_number_);
249
219k
  }
250
474k
  if (has_max_column_family_) {
251
37.7k
    PutVarint32(dst, kMaxColumnFamily, max_column_family_);
252
37.7k
  }
253
474k
  if (has_min_log_number_to_keep_) {
254
67.7k
    PutVarint32Varint64(dst, kMinLogNumberToKeep, min_log_number_to_keep_);
255
67.7k
  }
256
474k
  if (has_last_sequence_) {
257
279k
    PutVarint32Varint64(dst, kLastSequence, last_sequence_);
258
279k
  }
259
474k
  for (size_t i = 0; i < compact_cursors_.size(); i++) {
260
0
    if (compact_cursors_[i].second.Valid()) {
261
0
      PutVarint32(dst, kCompactCursor);
262
0
      PutVarint32(dst, compact_cursors_[i].first);  // level
263
0
      PutLengthPrefixedSlice(dst, compact_cursors_[i].second.Encode());
264
0
    }
265
0
  }
266
474k
  for (const auto& deleted : deleted_files_) {
267
11.9k
    PutVarint32Varint32Varint64(dst, kDeletedFile, deleted.first /* level */,
268
11.9k
                                deleted.second /* file number */);
269
11.9k
  }
270
271
474k
  bool min_log_num_written = false;
272
273
474k
  assert(new_files_.empty() || ts_sz.has_value());
274
568k
  for (size_t i = 0; i < new_files_.size(); i++) {
275
93.9k
    const FileMetaData& f = new_files_[i].second;
276
93.9k
    if (!f.smallest.Valid() || !f.largest.Valid() ||
277
93.9k
        f.epoch_number == kUnknownEpochNumber) {
278
0
      return false;
279
0
    }
280
93.9k
    EncodeToNewFile4(f, new_files_[i].first, ts_sz.value(),
281
93.9k
                     has_min_log_number_to_keep_, min_log_number_to_keep_,
282
93.9k
                     min_log_num_written, dst);
283
93.9k
  }
284
285
474k
  for (const auto& blob_file_addition : blob_file_additions_) {
286
0
    PutVarint32(dst, kBlobFileAddition);
287
0
    blob_file_addition.EncodeTo(dst);
288
0
  }
289
290
474k
  for (const auto& blob_file_garbage : blob_file_garbages_) {
291
0
    PutVarint32(dst, kBlobFileGarbage);
292
0
    blob_file_garbage.EncodeTo(dst);
293
0
  }
294
295
474k
  for (const auto& wal_addition : wal_additions_) {
296
0
    PutVarint32(dst, kWalAddition2);
297
0
    std::string encoded;
298
0
    wal_addition.EncodeTo(&encoded);
299
0
    PutLengthPrefixedSlice(dst, encoded);
300
0
  }
301
302
474k
  if (!wal_deletion_.IsEmpty()) {
303
29.2k
    PutVarint32(dst, kWalDeletion2);
304
29.2k
    std::string encoded;
305
29.2k
    wal_deletion_.EncodeTo(&encoded);
306
29.2k
    PutLengthPrefixedSlice(dst, encoded);
307
29.2k
  }
308
309
  // 0 is default and does not need to be explicitly written
310
474k
  if (column_family_ != 0) {
311
78.3k
    PutVarint32(dst, kColumnFamily, column_family_);
312
78.3k
  }
313
314
474k
  if (is_column_family_add_) {
315
31.3k
    PutVarint32(dst, kColumnFamilyAdd);
316
31.3k
    PutLengthPrefixedSlice(dst, Slice(column_family_name_));
317
31.3k
  }
318
319
474k
  if (is_column_family_drop_) {
320
15.6k
    PutVarint32(dst, kColumnFamilyDrop);
321
15.6k
  }
322
323
474k
  if (is_in_atomic_group_) {
324
0
    PutVarint32(dst, kInAtomicGroup);
325
0
    PutVarint32(dst, remaining_entries_);
326
0
  }
327
328
474k
  if (HasFullHistoryTsLow()) {
329
0
    PutVarint32(dst, kFullHistoryTsLow);
330
0
    PutLengthPrefixedSlice(dst, full_history_ts_low_);
331
0
  }
332
333
474k
  if (HasPersistUserDefinedTimestamps()) {
334
    // persist_user_defined_timestamps flag should be logged in the same
335
    // VersionEdit as the user comparator name.
336
76.2k
    assert(has_comparator_);
337
76.2k
    PutVarint32(dst, kPersistUserDefinedTimestamps);
338
76.2k
    char p = static_cast<char>(persist_user_defined_timestamps_);
339
76.2k
    PutLengthPrefixedSlice(dst, Slice(&p, 1));
340
76.2k
  }
341
342
474k
  if (HasSubcompactionProgress()) {
343
0
    PutVarint32(dst, kSubcompactionProgress);
344
0
    std::string progress_data;
345
0
    subcompaction_progress_.EncodeTo(&progress_data);
346
0
    PutLengthPrefixedSlice(dst, progress_data);
347
0
  }
348
349
474k
  if (has_last_compacted_manifest_file_size_) {
350
44.8k
    PutVarint32(dst, kLastCompactedManifestFileSize);
351
44.8k
    std::string varint_size;
352
44.8k
    PutVarint64(&varint_size, last_compacted_manifest_file_size_);
353
44.8k
    PutLengthPrefixedSlice(dst, Slice(varint_size));
354
44.8k
  }
355
356
474k
  if (has_committed_options_file_number_) {
357
0
    PutVarint32(dst, kCommittedOptionsFileNumber);
358
0
    std::string encoded;
359
0
    PutVarint64(&encoded, committed_options_file_number_);
360
0
    PutLengthPrefixedSlice(dst, encoded);
361
0
  }
362
363
474k
  for (const uint64_t blob_file_number : dropped_blob_files_) {
364
0
    PutVarint32(dst, kDroppedBlobFile);
365
0
    std::string encoded;
366
0
    PutVarint64(&encoded, blob_file_number);
367
0
    PutLengthPrefixedSlice(dst, encoded);
368
0
  }
369
370
474k
  return true;
371
474k
}
372
373
void VersionEdit::EncodeToNewFile4(const FileMetaData& f, int level,
374
                                   size_t ts_sz,
375
                                   bool has_min_log_number_to_keep,
376
                                   uint64_t min_log_number_to_keep,
377
                                   bool& min_log_num_written,
378
93.9k
                                   std::string* dst) {
379
93.9k
  PutVarint32(dst, kNewFile4);
380
93.9k
  PutVarint32Varint64(dst, level, f.fd.GetNumber());
381
93.9k
  PutVarint64(dst, f.fd.GetFileSize());
382
93.9k
  EncodeFileBoundaries(dst, f, ts_sz);
383
93.9k
  PutVarint64Varint64(dst, f.fd.smallest_seqno, f.fd.largest_seqno);
384
  // Customized fields' format:
385
  // +-----------------------------+
386
  // | 1st field's tag (varint32)  |
387
  // +-----------------------------+
388
  // | 1st field's size (varint32) |
389
  // +-----------------------------+
390
  // |    bytes for 1st field      |
391
  // |  (based on size decoded)    |
392
  // +-----------------------------+
393
  // |                             |
394
  // |          ......             |
395
  // |                             |
396
  // +-----------------------------+
397
  // | last field's size (varint32)|
398
  // +-----------------------------+
399
  // |    bytes for last field     |
400
  // |  (based on size decoded)    |
401
  // +-----------------------------+
402
  // | terminating tag (varint32)  |
403
  // +-----------------------------+
404
  //
405
  // Customized encoding for fields:
406
  //   tag kPathId: 1 byte as path_id
407
  //   tag kNeedCompaction:
408
  //        now only can take one char value 1 indicating need-compaction
409
  //
410
93.9k
  PutVarint32(dst, NewFileCustomTag::kOldestAncestorTime);
411
93.9k
  std::string varint_oldest_ancestor_time;
412
93.9k
  PutVarint64(&varint_oldest_ancestor_time, f.oldest_ancestor_time);
413
93.9k
  TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:VarintOldestAncestorTime",
414
93.9k
                           &varint_oldest_ancestor_time);
415
93.9k
  PutLengthPrefixedSlice(dst, Slice(varint_oldest_ancestor_time));
416
417
93.9k
  PutVarint32(dst, NewFileCustomTag::kFileCreationTime);
418
93.9k
  std::string varint_file_creation_time;
419
93.9k
  PutVarint64(&varint_file_creation_time, f.file_creation_time);
420
93.9k
  TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:VarintFileCreationTime",
421
93.9k
                           &varint_file_creation_time);
422
93.9k
  PutLengthPrefixedSlice(dst, Slice(varint_file_creation_time));
423
424
93.9k
  PutVarint32(dst, NewFileCustomTag::kEpochNumber);
425
93.9k
  std::string varint_epoch_number;
426
93.9k
  PutVarint64(&varint_epoch_number, f.epoch_number);
427
93.9k
  PutLengthPrefixedSlice(dst, Slice(varint_epoch_number));
428
429
93.9k
  if (f.file_checksum_func_name != kUnknownFileChecksumFuncName) {
430
0
    PutVarint32(dst, NewFileCustomTag::kFileChecksum);
431
0
    PutLengthPrefixedSlice(dst, Slice(f.file_checksum));
432
433
0
    PutVarint32(dst, NewFileCustomTag::kFileChecksumFuncName);
434
0
    PutLengthPrefixedSlice(dst, Slice(f.file_checksum_func_name));
435
0
  }
436
437
93.9k
  if (f.fd.GetPathId() != 0) {
438
0
    PutVarint32(dst, NewFileCustomTag::kPathId);
439
0
    char p = static_cast<char>(f.fd.GetPathId());
440
0
    PutLengthPrefixedSlice(dst, Slice(&p, 1));
441
0
  }
442
93.9k
  if (f.temperature != Temperature::kUnknown) {
443
0
    PutVarint32(dst, NewFileCustomTag::kTemperature);
444
0
    char p = static_cast<char>(f.temperature);
445
0
    PutLengthPrefixedSlice(dst, Slice(&p, 1));
446
0
  }
447
93.9k
  if (f.marked_for_compaction) {
448
0
    PutVarint32(dst, NewFileCustomTag::kNeedCompaction);
449
0
    char p = static_cast<char>(1);
450
0
    PutLengthPrefixedSlice(dst, Slice(&p, 1));
451
0
  }
452
93.9k
  if (has_min_log_number_to_keep && !min_log_num_written) {
453
24.2k
    PutVarint32(dst, NewFileCustomTag::kMinLogNumberToKeepHack);
454
24.2k
    std::string varint_log_number;
455
24.2k
    PutFixed64(&varint_log_number, min_log_number_to_keep);
456
24.2k
    PutLengthPrefixedSlice(dst, Slice(varint_log_number));
457
24.2k
    min_log_num_written = true;
458
24.2k
  }
459
93.9k
  if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {
460
0
    PutVarint32(dst, NewFileCustomTag::kOldestBlobFileNumber);
461
0
    std::string oldest_blob_file_number;
462
0
    PutVarint64(&oldest_blob_file_number, f.oldest_blob_file_number);
463
0
    PutLengthPrefixedSlice(dst, Slice(oldest_blob_file_number));
464
0
  }
465
93.9k
  UniqueId64x2 unique_id = f.unique_id;
466
93.9k
  TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:UniqueId", &unique_id);
467
93.9k
  if (unique_id != kNullUniqueId64x2) {
468
93.9k
    PutVarint32(dst, NewFileCustomTag::kUniqueId);
469
93.9k
    std::string unique_id_str = EncodeUniqueIdBytes(&unique_id);
470
93.9k
    PutLengthPrefixedSlice(dst, Slice(unique_id_str));
471
93.9k
  }
472
93.9k
  if (f.compensated_range_deletion_size) {
473
0
    PutVarint32(dst, NewFileCustomTag::kCompensatedRangeDeletionSize);
474
0
    std::string compensated_range_deletion_size;
475
0
    PutVarint64(&compensated_range_deletion_size,
476
0
                f.compensated_range_deletion_size);
477
0
    PutLengthPrefixedSlice(dst, Slice(compensated_range_deletion_size));
478
0
  }
479
93.9k
  if (f.tail_size) {
480
93.9k
    PutVarint32(dst, NewFileCustomTag::kTailSize);
481
93.9k
    std::string varint_tail_size;
482
93.9k
    PutVarint64(&varint_tail_size, f.tail_size);
483
93.9k
    PutLengthPrefixedSlice(dst, Slice(varint_tail_size));
484
93.9k
  }
485
93.9k
  if (!f.user_defined_timestamps_persisted) {
486
    // The default value for the flag is true, it's only explicitly persisted
487
    // when it's false. We are putting 0 as the value here to signal false
488
    // (i.e. UDTS not persisted).
489
0
    PutVarint32(dst, NewFileCustomTag::kUserDefinedTimestampsPersisted);
490
0
    char p = static_cast<char>(0);
491
0
    PutLengthPrefixedSlice(dst, Slice(&p, 1));
492
0
  }
493
  // Encode min/max timestamp if they are non-empty
494
93.9k
  if (!f.min_timestamp.empty()) {
495
0
    PutVarint32(dst, NewFileCustomTag::kMinTimestamp);
496
0
    PutLengthPrefixedSlice(dst, Slice(f.min_timestamp));
497
0
  }
498
93.9k
  if (!f.max_timestamp.empty()) {
499
0
    PutVarint32(dst, NewFileCustomTag::kMaxTimestamp);
500
0
    PutLengthPrefixedSlice(dst, Slice(f.max_timestamp));
501
0
  }
502
93.9k
  if (!f.file_open_metadata.empty()) {
503
0
    PutVarint32(dst, NewFileCustomTag::kFileOpenMetadata);
504
0
    PutLengthPrefixedSlice(dst, Slice(f.file_open_metadata));
505
0
  }
506
93.9k
  if (f.db_ownership_start_time != kUnknownDbOwnershipStartTime) {
507
93.9k
    PutVarint32(dst, NewFileCustomTag::kDbOwnershipStartTime);
508
93.9k
    std::string varint_db_ownership_start_time;
509
93.9k
    PutVarint64(&varint_db_ownership_start_time, f.db_ownership_start_time);
510
93.9k
    PutLengthPrefixedSlice(dst, Slice(varint_db_ownership_start_time));
511
93.9k
  }
512
93.9k
  if (f.data_file_provenance != 0) {
513
93.9k
    PutVarint32(dst, NewFileCustomTag::kDataFileProvenance);
514
93.9k
    std::string varint_data_file_provenance;
515
93.9k
    PutVarint32(&varint_data_file_provenance, f.data_file_provenance);
516
93.9k
    PutLengthPrefixedSlice(dst, Slice(varint_data_file_provenance));
517
93.9k
  }
518
93.9k
  TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:NewFile4:CustomizeFields",
519
93.9k
                           dst);
520
521
93.9k
  PutVarint32(dst, NewFileCustomTag::kTerminate);
522
93.9k
}
523
155k
static bool GetInternalKey(Slice* input, InternalKey* dst) {
524
155k
  Slice str;
525
155k
  if (GetLengthPrefixedSlice(input, &str)) {
526
155k
    dst->DecodeFrom(str);
527
155k
    return dst->Valid();
528
155k
  } else {
529
0
    return false;
530
0
  }
531
155k
}
532
533
86.4k
bool VersionEdit::GetLevel(Slice* input, int* level, int& max_level) {
534
86.4k
  uint32_t v = 0;
535
86.4k
  if (GetVarint32(input, &v)) {
536
86.4k
    *level = v;
537
86.4k
    if (max_level < *level) {
538
12.1k
      max_level = *level;
539
12.1k
    }
540
86.4k
    return true;
541
86.4k
  } else {
542
0
    return false;
543
0
  }
544
86.4k
}
545
546
const char* VersionEdit::DecodeNewFile4From(Slice* input, int& max_level,
547
                                            uint64_t& min_log_number_to_keep,
548
                                            bool& has_min_log_number_to_keep,
549
                                            NewFiles& new_files,
550
77.5k
                                            FileMetaData& f) {
551
77.5k
  int level = 0;
552
77.5k
  uint64_t number = 0;
553
77.5k
  uint32_t path_id = 0;
554
77.5k
  uint64_t file_size = 0;
555
77.5k
  SequenceNumber smallest_seqno = 0;
556
77.5k
  SequenceNumber largest_seqno = kMaxSequenceNumber;
557
77.5k
  if (GetLevel(input, &level, max_level) && GetVarint64(input, &number) &&
558
77.5k
      GetVarint64(input, &file_size) && GetInternalKey(input, &f.smallest) &&
559
77.5k
      GetInternalKey(input, &f.largest) &&
560
77.5k
      GetVarint64(input, &smallest_seqno) &&
561
77.5k
      GetVarint64(input, &largest_seqno)) {
562
    // See comments in VersionEdit::EncodeTo() for format of customized fields
563
642k
    while (true) {
564
642k
      uint32_t custom_tag = 0;
565
642k
      Slice field;
566
642k
      if (!GetVarint32(input, &custom_tag)) {
567
0
        return "new-file4 custom field";
568
0
      }
569
642k
      if (custom_tag == kTerminate) {
570
77.5k
        break;
571
77.5k
      }
572
564k
      if (!GetLengthPrefixedSlice(input, &field)) {
573
0
        return "new-file4 custom field length prefixed slice error";
574
0
      }
575
564k
      switch (custom_tag) {
576
0
        case kPathId:
577
0
          if (field.size() != 1) {
578
0
            return "path_id field wrong size";
579
0
          }
580
0
          path_id = field[0];
581
0
          if (path_id > 3) {
582
0
            return "path_id wrong vaue";
583
0
          }
584
0
          break;
585
77.5k
        case kOldestAncestorTime:
586
77.5k
          if (!GetVarint64(&field, &f.oldest_ancestor_time)) {
587
0
            return "invalid oldest ancestor time";
588
0
          }
589
77.5k
          break;
590
77.5k
        case kFileCreationTime:
591
77.5k
          if (!GetVarint64(&field, &f.file_creation_time)) {
592
0
            return "invalid file creation time";
593
0
          }
594
77.5k
          break;
595
77.5k
        case kEpochNumber:
596
77.5k
          if (!GetVarint64(&field, &f.epoch_number)) {
597
0
            return "invalid epoch number";
598
0
          }
599
77.5k
          break;
600
77.5k
        case kFileChecksum:
601
0
          f.file_checksum = field.ToString();
602
0
          break;
603
0
        case kFileChecksumFuncName:
604
0
          f.file_checksum_func_name = field.ToString();
605
0
          break;
606
0
        case kNeedCompaction:
607
0
          if (field.size() != 1) {
608
0
            return "need_compaction field wrong size";
609
0
          }
610
0
          f.marked_for_compaction = (field[0] == 1);
611
0
          break;
612
21.6k
        case kMinLogNumberToKeepHack:
613
          // This is a hack to encode kMinLogNumberToKeep in a
614
          // forward-compatible fashion.
615
21.6k
          if (!GetFixed64(&field, &min_log_number_to_keep)) {
616
0
            return "deleted log number malformatted";
617
0
          }
618
21.6k
          has_min_log_number_to_keep = true;
619
21.6k
          break;
620
0
        case kOldestBlobFileNumber:
621
0
          if (!GetVarint64(&field, &f.oldest_blob_file_number)) {
622
0
            return "invalid oldest blob file number";
623
0
          }
624
0
          break;
625
0
        case kTemperature:
626
0
          if (field.size() != 1) {
627
0
            return "temperature field wrong size";
628
0
          } else {
629
0
            Temperature casted_field = static_cast<Temperature>(field[0]);
630
0
            if (casted_field < Temperature::kLastTemperature) {
631
0
              f.temperature = casted_field;
632
0
            }
633
0
          }
634
0
          break;
635
77.5k
        case kUniqueId:
636
77.5k
          if (!DecodeUniqueIdBytes(field.ToString(), &f.unique_id).ok()) {
637
0
            f.unique_id = kNullUniqueId64x2;
638
0
            return "invalid unique id";
639
0
          }
640
77.5k
          break;
641
77.5k
        case kCompensatedRangeDeletionSize:
642
0
          if (!GetVarint64(&field, &f.compensated_range_deletion_size)) {
643
0
            return "Invalid compensated range deletion size";
644
0
          }
645
0
          break;
646
77.5k
        case kTailSize:
647
77.5k
          if (!GetVarint64(&field, &f.tail_size)) {
648
0
            return "invalid tail start offset";
649
0
          }
650
77.5k
          break;
651
77.5k
        case kUserDefinedTimestampsPersisted:
652
0
          if (field.size() != 1) {
653
0
            return "user-defined timestamps persisted field wrong size";
654
0
          }
655
0
          f.user_defined_timestamps_persisted = (field[0] == 1);
656
0
          break;
657
0
        case kMinTimestamp:
658
0
          f.min_timestamp = field.ToString();
659
0
          break;
660
0
        case kMaxTimestamp:
661
0
          f.max_timestamp = field.ToString();
662
0
          break;
663
0
        case kFileOpenMetadata:
664
0
          f.file_open_metadata = field.ToString();
665
0
          break;
666
77.5k
        case kDbOwnershipStartTime:
667
77.5k
          if (!GetVarint64(&field, &f.db_ownership_start_time)) {
668
0
            return "invalid DB ownership start time";
669
0
          }
670
77.5k
          break;
671
77.5k
        case kDataFileProvenance:
672
77.5k
          if (!GetVarint32(&field, &f.data_file_provenance)) {
673
0
            return "invalid data file provenance";
674
0
          }
675
77.5k
          break;
676
77.5k
        default:
677
0
          if ((custom_tag & kCustomTagNonSafeIgnoreMask) != 0) {
678
            // Should not proceed if cannot understand it
679
0
            return "new-file4 custom field not supported";
680
0
          }
681
0
          break;
682
564k
      }
683
564k
    }
684
77.5k
  } else {
685
0
    return "new-file4 entry";
686
0
  }
687
77.5k
  f.fd =
688
77.5k
      FileDescriptor(number, path_id, file_size, smallest_seqno, largest_seqno);
689
77.5k
  new_files.emplace_back(level, f);
690
77.5k
  return nullptr;
691
77.5k
}
692
693
void VersionEdit::EncodeFileBoundaries(std::string* dst,
694
93.9k
                                       const FileMetaData& meta, size_t ts_sz) {
695
93.9k
  if (ts_sz == 0 || meta.user_defined_timestamps_persisted) {
696
93.9k
    PutLengthPrefixedSlice(dst, meta.smallest.Encode());
697
93.9k
    PutLengthPrefixedSlice(dst, meta.largest.Encode());
698
93.9k
    return;
699
93.9k
  }
700
0
  std::string smallest_buf;
701
0
  std::string largest_buf;
702
0
  StripTimestampFromInternalKey(&smallest_buf, meta.smallest.Encode(), ts_sz);
703
0
  StripTimestampFromInternalKey(&largest_buf, meta.largest.Encode(), ts_sz);
704
0
  PutLengthPrefixedSlice(dst, smallest_buf);
705
0
  PutLengthPrefixedSlice(dst, largest_buf);
706
0
}
707
708
393k
Status VersionEdit::DecodeFrom(const Slice& src) {
709
393k
  Clear();
710
#ifndef NDEBUG
711
  bool ignore_ignorable_tags = false;
712
  TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:IgnoreIgnorableTags",
713
                           &ignore_ignorable_tags);
714
#endif
715
393k
  Slice input = src;
716
393k
  const char* msg = nullptr;
717
393k
  uint32_t tag = 0;
718
719
  // Temporary storage for parsing
720
393k
  int level = 0;
721
393k
  FileMetaData f;
722
393k
  Slice str;
723
393k
  InternalKey key;
724
1.60M
  while (msg == nullptr && GetVarint32(&input, &tag)) {
725
#ifndef NDEBUG
726
    if (ignore_ignorable_tags && tag > kTagSafeIgnoreMask) {
727
      tag = kTagSafeIgnoreMask;
728
    }
729
#endif
730
1.20M
    switch (tag) {
731
44.8k
      case kDbId:
732
44.8k
        if (GetLengthPrefixedSlice(&input, &str)) {
733
44.8k
          db_id_ = str.ToString();
734
44.8k
          has_db_id_ = true;
735
44.8k
        } else {
736
0
          msg = "db id";
737
0
        }
738
44.8k
        break;
739
66.4k
      case kComparator:
740
66.4k
        if (GetLengthPrefixedSlice(&input, &str)) {
741
66.4k
          comparator_ = str.ToString();
742
66.4k
          has_comparator_ = true;
743
66.4k
        } else {
744
0
          msg = "comparator name";
745
0
        }
746
66.4k
        break;
747
748
118k
      case kLogNumber:
749
118k
        if (GetVarint64(&input, &log_number_)) {
750
118k
          has_log_number_ = true;
751
118k
        } else {
752
0
          msg = "log number";
753
0
        }
754
118k
        break;
755
756
143k
      case kPrevLogNumber:
757
143k
        if (GetVarint64(&input, &prev_log_number_)) {
758
143k
          has_prev_log_number_ = true;
759
143k
        } else {
760
0
          msg = "previous log number";
761
0
        }
762
143k
        break;
763
764
181k
      case kNextFileNumber:
765
181k
        if (GetVarint64(&input, &next_file_number_)) {
766
181k
          has_next_file_number_ = true;
767
181k
        } else {
768
0
          msg = "next file number";
769
0
        }
770
181k
        break;
771
772
33.6k
      case kMaxColumnFamily:
773
33.6k
        if (GetVarint32(&input, &max_column_family_)) {
774
33.6k
          has_max_column_family_ = true;
775
33.6k
        } else {
776
0
          msg = "max column family";
777
0
        }
778
33.6k
        break;
779
780
56.2k
      case kMinLogNumberToKeep:
781
56.2k
        if (GetVarint64(&input, &min_log_number_to_keep_)) {
782
56.2k
          has_min_log_number_to_keep_ = true;
783
56.2k
        } else {
784
0
          msg = "min log number to kee";
785
0
        }
786
56.2k
        break;
787
788
232k
      case kLastSequence:
789
232k
        if (GetVarint64(&input, &last_sequence_)) {
790
232k
          has_last_sequence_ = true;
791
232k
        } else {
792
0
          msg = "last sequence number";
793
0
        }
794
232k
        break;
795
796
0
      case kCompactCursor:
797
0
        if (GetLevel(&input, &level, max_level_) &&
798
0
            GetInternalKey(&input, &key)) {
799
          // Here we re-use the output format of compact pointer in LevelDB
800
          // to persist compact_cursors_
801
0
          compact_cursors_.push_back(std::make_pair(level, key));
802
0
        } else {
803
0
          if (!msg) {
804
0
            msg = "compaction cursor";
805
0
          }
806
0
        }
807
0
        break;
808
809
8.86k
      case kDeletedFile: {
810
8.86k
        uint64_t number = 0;
811
8.86k
        if (GetLevel(&input, &level, max_level_) &&
812
8.86k
            GetVarint64(&input, &number)) {
813
8.86k
          deleted_files_.insert(std::make_pair(level, number));
814
8.86k
        } else {
815
0
          if (!msg) {
816
0
            msg = "deleted file";
817
0
          }
818
0
        }
819
8.86k
        break;
820
0
      }
821
822
0
      case kNewFile: {
823
0
        uint64_t number = 0;
824
0
        uint64_t file_size = 0;
825
0
        if (GetLevel(&input, &level, max_level_) &&
826
0
            GetVarint64(&input, &number) && GetVarint64(&input, &file_size) &&
827
0
            GetInternalKey(&input, &f.smallest) &&
828
0
            GetInternalKey(&input, &f.largest)) {
829
0
          f.fd = FileDescriptor(number, 0, file_size);
830
0
          new_files_.push_back(std::make_pair(level, f));
831
0
        } else {
832
0
          if (!msg) {
833
0
            msg = "new-file entry";
834
0
          }
835
0
        }
836
0
        break;
837
0
      }
838
0
      case kNewFile2: {
839
0
        uint64_t number = 0;
840
0
        uint64_t file_size = 0;
841
0
        SequenceNumber smallest_seqno = 0;
842
0
        SequenceNumber largest_seqno = kMaxSequenceNumber;
843
0
        if (GetLevel(&input, &level, max_level_) &&
844
0
            GetVarint64(&input, &number) && GetVarint64(&input, &file_size) &&
845
0
            GetInternalKey(&input, &f.smallest) &&
846
0
            GetInternalKey(&input, &f.largest) &&
847
0
            GetVarint64(&input, &smallest_seqno) &&
848
0
            GetVarint64(&input, &largest_seqno)) {
849
0
          f.fd = FileDescriptor(number, 0, file_size, smallest_seqno,
850
0
                                largest_seqno);
851
0
          new_files_.push_back(std::make_pair(level, f));
852
0
        } else {
853
0
          if (!msg) {
854
0
            msg = "new-file2 entry";
855
0
          }
856
0
        }
857
0
        break;
858
0
      }
859
860
0
      case kNewFile3: {
861
0
        uint64_t number = 0;
862
0
        uint32_t path_id = 0;
863
0
        uint64_t file_size = 0;
864
0
        SequenceNumber smallest_seqno = 0;
865
0
        SequenceNumber largest_seqno = kMaxSequenceNumber;
866
0
        if (GetLevel(&input, &level, max_level_) &&
867
0
            GetVarint64(&input, &number) && GetVarint32(&input, &path_id) &&
868
0
            GetVarint64(&input, &file_size) &&
869
0
            GetInternalKey(&input, &f.smallest) &&
870
0
            GetInternalKey(&input, &f.largest) &&
871
0
            GetVarint64(&input, &smallest_seqno) &&
872
0
            GetVarint64(&input, &largest_seqno)) {
873
0
          f.fd = FileDescriptor(number, path_id, file_size, smallest_seqno,
874
0
                                largest_seqno);
875
0
          new_files_.push_back(std::make_pair(level, f));
876
0
        } else {
877
0
          if (!msg) {
878
0
            msg = "new-file3 entry";
879
0
          }
880
0
        }
881
0
        break;
882
0
      }
883
884
77.5k
      case kNewFile4: {
885
77.5k
        FileMetaData ignored_file;
886
77.5k
        msg = DecodeNewFile4From(&input, max_level_, min_log_number_to_keep_,
887
77.5k
                                 has_min_log_number_to_keep_, new_files_,
888
77.5k
                                 ignored_file);
889
77.5k
        break;
890
0
      }
891
892
0
      case kBlobFileAddition:
893
0
      case kBlobFileAddition_DEPRECATED: {
894
0
        BlobFileAddition blob_file_addition;
895
0
        const Status s = blob_file_addition.DecodeFrom(&input);
896
0
        if (!s.ok()) {
897
0
          return s;
898
0
        }
899
900
0
        AddBlobFile(std::move(blob_file_addition));
901
0
        break;
902
0
      }
903
904
0
      case kBlobFileGarbage:
905
0
      case kBlobFileGarbage_DEPRECATED: {
906
0
        BlobFileGarbage blob_file_garbage;
907
0
        const Status s = blob_file_garbage.DecodeFrom(&input);
908
0
        if (!s.ok()) {
909
0
          return s;
910
0
        }
911
912
0
        AddBlobFileGarbage(std::move(blob_file_garbage));
913
0
        break;
914
0
      }
915
916
0
      case kWalAddition: {
917
0
        WalAddition wal_addition;
918
0
        const Status s = wal_addition.DecodeFrom(&input);
919
0
        if (!s.ok()) {
920
0
          return s;
921
0
        }
922
923
0
        wal_additions_.emplace_back(std::move(wal_addition));
924
0
        break;
925
0
      }
926
927
0
      case kWalAddition2: {
928
0
        Slice encoded;
929
0
        if (!GetLengthPrefixedSlice(&input, &encoded)) {
930
0
          msg = "WalAddition not prefixed by length";
931
0
          break;
932
0
        }
933
934
0
        WalAddition wal_addition;
935
0
        const Status s = wal_addition.DecodeFrom(&encoded);
936
0
        if (!s.ok()) {
937
0
          return s;
938
0
        }
939
940
0
        wal_additions_.emplace_back(std::move(wal_addition));
941
0
        break;
942
0
      }
943
944
0
      case kWalDeletion: {
945
0
        WalDeletion wal_deletion;
946
0
        const Status s = wal_deletion.DecodeFrom(&input);
947
0
        if (!s.ok()) {
948
0
          return s;
949
0
        }
950
951
0
        wal_deletion_ = std::move(wal_deletion);
952
0
        break;
953
0
      }
954
955
26.2k
      case kWalDeletion2: {
956
26.2k
        Slice encoded;
957
26.2k
        if (!GetLengthPrefixedSlice(&input, &encoded)) {
958
0
          msg = "WalDeletion not prefixed by length";
959
0
          break;
960
0
        }
961
962
26.2k
        WalDeletion wal_deletion;
963
26.2k
        const Status s = wal_deletion.DecodeFrom(&encoded);
964
26.2k
        if (!s.ok()) {
965
0
          return s;
966
0
        }
967
968
26.2k
        wal_deletion_ = std::move(wal_deletion);
969
26.2k
        break;
970
26.2k
      }
971
972
71.7k
      case kColumnFamily:
973
71.7k
        if (!GetVarint32(&input, &column_family_)) {
974
0
          if (!msg) {
975
0
            msg = "set column family id";
976
0
          }
977
0
        }
978
71.7k
        break;
979
980
29.7k
      case kColumnFamilyAdd:
981
29.7k
        if (GetLengthPrefixedSlice(&input, &str)) {
982
29.7k
          is_column_family_add_ = true;
983
29.7k
          column_family_name_ = str.ToString();
984
29.7k
        } else {
985
0
          if (!msg) {
986
0
            msg = "column family add";
987
0
          }
988
0
        }
989
29.7k
        break;
990
991
14.0k
      case kColumnFamilyDrop:
992
14.0k
        is_column_family_drop_ = true;
993
14.0k
        break;
994
995
0
      case kInAtomicGroup:
996
0
        is_in_atomic_group_ = true;
997
0
        if (!GetVarint32(&input, &remaining_entries_)) {
998
0
          if (!msg) {
999
0
            msg = "remaining entries";
1000
0
          }
1001
0
        }
1002
0
        break;
1003
1004
0
      case kFullHistoryTsLow:
1005
0
        if (!GetLengthPrefixedSlice(&input, &str)) {
1006
0
          msg = "full_history_ts_low";
1007
0
        } else if (str.empty()) {
1008
0
          msg = "full_history_ts_low: empty";
1009
0
        } else {
1010
0
          full_history_ts_low_.assign(str.data(), str.size());
1011
0
        }
1012
0
        break;
1013
1014
66.4k
      case kPersistUserDefinedTimestamps:
1015
66.4k
        if (!GetLengthPrefixedSlice(&input, &str)) {
1016
0
          msg = "persist_user_defined_timestamps";
1017
66.4k
        } else if (str.size() != 1) {
1018
0
          msg = "persist_user_defined_timestamps field wrong size";
1019
66.4k
        } else {
1020
66.4k
          persist_user_defined_timestamps_ = (str[0] == 1);
1021
66.4k
          has_persist_user_defined_timestamps_ = true;
1022
66.4k
        }
1023
66.4k
        break;
1024
1025
0
      case kSubcompactionProgress: {
1026
0
        Slice encoded;
1027
0
        if (!GetLengthPrefixedSlice(&input, &encoded)) {
1028
0
          msg = "SubcompactionProgress not prefixed by length";
1029
0
          break;
1030
0
        }
1031
1032
0
        SubcompactionProgress progress;
1033
0
        Status s = progress.DecodeFrom(&encoded);
1034
0
        if (!s.ok()) {
1035
0
          return s;
1036
0
        }
1037
1038
0
        SetSubcompactionProgress(progress);
1039
0
        break;
1040
0
      }
1041
1042
36.7k
      case kLastCompactedManifestFileSize: {
1043
36.7k
        Slice encoded;
1044
36.7k
        if (GetLengthPrefixedSlice(&input, &encoded) &&
1045
36.7k
            GetVarint64(&encoded, &last_compacted_manifest_file_size_)) {
1046
36.7k
          has_last_compacted_manifest_file_size_ = true;
1047
36.7k
        } else {
1048
0
          msg = "last compacted manifest file size";
1049
0
        }
1050
36.7k
        break;
1051
0
      }
1052
1053
0
      case kCommittedOptionsFileNumber: {
1054
0
        Slice encoded;
1055
0
        if (GetLengthPrefixedSlice(&input, &encoded) &&
1056
0
            GetVarint64(&encoded, &committed_options_file_number_) &&
1057
0
            encoded.empty()) {
1058
0
          has_committed_options_file_number_ = true;
1059
0
        } else {
1060
0
          msg = "committed options file number";
1061
0
        }
1062
0
        break;
1063
0
      }
1064
1065
0
      case kDroppedBlobFile: {
1066
0
        Slice encoded;
1067
0
        uint64_t blob_file_number = 0;
1068
0
        if (GetLengthPrefixedSlice(&input, &encoded) &&
1069
0
            GetVarint64(&encoded, &blob_file_number) && encoded.empty()) {
1070
0
          dropped_blob_files_.push_back(blob_file_number);
1071
0
        } else {
1072
0
          msg = "dropped blob file";
1073
0
        }
1074
0
        break;
1075
0
      }
1076
1077
0
      default:
1078
0
        if (tag & kTagSafeIgnoreMask) {
1079
          // Tag from future which can be safely ignored.
1080
          // The next field must be the length of the entry.
1081
0
          uint32_t field_len;
1082
0
          if (!GetVarint32(&input, &field_len) ||
1083
0
              static_cast<size_t>(field_len) > input.size()) {
1084
0
            if (!msg) {
1085
0
              msg = "safely ignoreable tag length error";
1086
0
            }
1087
0
          } else {
1088
0
            input.remove_prefix(static_cast<size_t>(field_len));
1089
0
          }
1090
0
        } else {
1091
0
          msg = "unknown tag";
1092
0
        }
1093
0
        break;
1094
1.20M
    }
1095
1.20M
  }
1096
1097
393k
  if (msg == nullptr && !input.empty()) {
1098
0
    msg = "invalid tag";
1099
0
  }
1100
1101
393k
  Status result;
1102
393k
  if (msg != nullptr) {
1103
0
    result = Status::Corruption("VersionEdit", msg);
1104
0
  }
1105
393k
  return result;
1106
393k
}
1107
1108
0
std::string VersionEdit::DebugString(bool hex_key) const {
1109
0
  std::string r;
1110
0
  r.append("VersionEdit {");
1111
0
  if (has_db_id_) {
1112
0
    r.append("\n  DB ID: ");
1113
0
    r.append(db_id_);
1114
0
  }
1115
0
  if (has_comparator_) {
1116
0
    r.append("\n  Comparator: ");
1117
0
    r.append(comparator_);
1118
0
  }
1119
0
  if (has_persist_user_defined_timestamps_) {
1120
0
    r.append("\n  PersistUserDefinedTimestamps: ");
1121
0
    r.append(persist_user_defined_timestamps_ ? "true" : "false");
1122
0
  }
1123
0
  if (has_log_number_) {
1124
0
    r.append("\n  LogNumber: ");
1125
0
    AppendNumberTo(&r, log_number_);
1126
0
  }
1127
0
  if (has_prev_log_number_) {
1128
0
    r.append("\n  PrevLogNumber: ");
1129
0
    AppendNumberTo(&r, prev_log_number_);
1130
0
  }
1131
0
  if (has_next_file_number_) {
1132
0
    r.append("\n  NextFileNumber: ");
1133
0
    AppendNumberTo(&r, next_file_number_);
1134
0
  }
1135
0
  if (has_max_column_family_) {
1136
0
    r.append("\n  MaxColumnFamily: ");
1137
0
    AppendNumberTo(&r, max_column_family_);
1138
0
  }
1139
0
  if (has_min_log_number_to_keep_) {
1140
0
    r.append("\n  MinLogNumberToKeep: ");
1141
0
    AppendNumberTo(&r, min_log_number_to_keep_);
1142
0
  }
1143
0
  if (has_last_sequence_) {
1144
0
    r.append("\n  LastSeq: ");
1145
0
    AppendNumberTo(&r, last_sequence_);
1146
0
  }
1147
0
  for (const auto& level_and_compact_cursor : compact_cursors_) {
1148
0
    r.append("\n  CompactCursor: ");
1149
0
    AppendNumberTo(&r, level_and_compact_cursor.first);
1150
0
    r.append(" ");
1151
0
    r.append(level_and_compact_cursor.second.DebugString(hex_key));
1152
0
  }
1153
0
  for (const auto& deleted_file : deleted_files_) {
1154
0
    r.append("\n  DeleteFile: ");
1155
0
    AppendNumberTo(&r, deleted_file.first);
1156
0
    r.append(" ");
1157
0
    AppendNumberTo(&r, deleted_file.second);
1158
0
  }
1159
0
  for (size_t i = 0; i < new_files_.size(); i++) {
1160
0
    const FileMetaData& f = new_files_[i].second;
1161
0
    r.append("\n  AddFile: ");
1162
0
    AppendNumberTo(&r, new_files_[i].first);
1163
0
    r.append(" ");
1164
0
    AppendNumberTo(&r, f.fd.GetNumber());
1165
0
    r.append(" ");
1166
0
    AppendNumberTo(&r, f.fd.GetFileSize());
1167
0
    r.append(" ");
1168
0
    r.append(f.smallest.DebugString(hex_key));
1169
0
    r.append(" .. ");
1170
0
    r.append(f.largest.DebugString(hex_key));
1171
0
    if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {
1172
0
      r.append(" blob_file:");
1173
0
      AppendNumberTo(&r, f.oldest_blob_file_number);
1174
0
    }
1175
    // Misspelled label kept for compatibility with tools parsing this output
1176
0
    r.append(" oldest_ancester_time:");
1177
0
    AppendNumberTo(&r, f.oldest_ancestor_time);
1178
0
    r.append(" file_creation_time:");
1179
0
    AppendNumberTo(&r, f.file_creation_time);
1180
0
    r.append(" epoch_number:");
1181
0
    AppendNumberTo(&r, f.epoch_number);
1182
0
    if (f.db_ownership_start_time != kUnknownDbOwnershipStartTime) {
1183
0
      r.append(" db_ownership_start_time:");
1184
0
      AppendNumberTo(&r, f.db_ownership_start_time);
1185
0
    }
1186
0
    if (f.data_file_provenance != 0) {
1187
0
      r.append(" data_file_provenance:");
1188
0
      r.append(DataFileProvenanceToString(f.data_file_provenance));
1189
0
    }
1190
0
    r.append(" file_checksum:");
1191
0
    r.append(Slice(f.file_checksum).ToString(true));
1192
0
    r.append(" file_checksum_func_name: ");
1193
0
    r.append(f.file_checksum_func_name);
1194
0
    if (f.temperature != Temperature::kUnknown) {
1195
0
      r.append(" temperature: ");
1196
      // Maybe change to human readable format whenthe feature becomes
1197
      // permanent
1198
0
      r.append(std::to_string(static_cast<int>(f.temperature)));
1199
0
    }
1200
0
    if (f.unique_id != kNullUniqueId64x2) {
1201
0
      r.append(" unique_id(internal): ");
1202
0
      UniqueId64x2 id = f.unique_id;
1203
0
      r.append(InternalUniqueIdToHumanString(&id));
1204
0
      r.append(" public_unique_id: ");
1205
0
      InternalUniqueIdToExternal(&id);
1206
0
      r.append(UniqueIdToHumanString(EncodeUniqueIdBytes(&id)));
1207
0
    }
1208
0
    r.append(" tail size: ");
1209
0
    AppendNumberTo(&r, f.tail_size);
1210
0
    r.append(" User-defined timestamps persisted: ");
1211
0
    r.append(f.user_defined_timestamps_persisted ? "true" : "false");
1212
0
    if (!f.file_open_metadata.empty()) {
1213
0
      r.append(" file_open_metadata_size: ");
1214
0
      AppendNumberTo(&r, f.file_open_metadata.size());
1215
0
    }
1216
0
  }
1217
1218
0
  for (const auto& blob_file_addition : blob_file_additions_) {
1219
0
    r.append("\n  BlobFileAddition: ");
1220
0
    r.append(blob_file_addition.DebugString());
1221
0
  }
1222
1223
0
  for (const auto& blob_file_garbage : blob_file_garbages_) {
1224
0
    r.append("\n  BlobFileGarbage: ");
1225
0
    r.append(blob_file_garbage.DebugString());
1226
0
  }
1227
1228
0
  for (const auto& wal_addition : wal_additions_) {
1229
0
    r.append("\n  WalAddition: ");
1230
0
    r.append(wal_addition.DebugString());
1231
0
  }
1232
1233
0
  if (!wal_deletion_.IsEmpty()) {
1234
0
    r.append("\n  WalDeletion: ");
1235
0
    r.append(wal_deletion_.DebugString());
1236
0
  }
1237
1238
0
  r.append("\n  ColumnFamily: ");
1239
0
  AppendNumberTo(&r, column_family_);
1240
0
  if (is_column_family_add_) {
1241
0
    r.append("\n  ColumnFamilyAdd: ");
1242
0
    r.append(column_family_name_);
1243
0
  }
1244
0
  if (is_column_family_drop_) {
1245
0
    r.append("\n  ColumnFamilyDrop");
1246
0
  }
1247
0
  if (is_in_atomic_group_) {
1248
0
    r.append("\n  AtomicGroup: ");
1249
0
    AppendNumberTo(&r, remaining_entries_);
1250
0
    r.append(" entries remains");
1251
0
  }
1252
0
  if (HasFullHistoryTsLow()) {
1253
0
    r.append("\n FullHistoryTsLow: ");
1254
0
    r.append(Slice(full_history_ts_low_).ToString(hex_key));
1255
0
  }
1256
0
  if (HasSubcompactionProgress()) {
1257
0
    r.append("\n SubcompactionProgress: ");
1258
0
    r.append(subcompaction_progress_.ToString());
1259
0
  }
1260
0
  if (has_last_compacted_manifest_file_size_) {
1261
0
    r.append("\n  LastCompactedManifestFileSize: ");
1262
0
    AppendNumberTo(&r, last_compacted_manifest_file_size_);
1263
0
  }
1264
0
  if (has_committed_options_file_number_) {
1265
0
    r.append("\n  CommittedOptionsFileNumber: ");
1266
0
    AppendNumberTo(&r, committed_options_file_number_);
1267
0
  }
1268
0
  for (const uint64_t blob_file_number : dropped_blob_files_) {
1269
0
    r.append("\n  DroppedBlobFile: ");
1270
0
    AppendNumberTo(&r, blob_file_number);
1271
0
  }
1272
0
  r.append("\n}\n");
1273
0
  return r;
1274
0
}
1275
1276
0
std::string VersionEdit::DebugJSON(int edit_num, bool hex_key) const {
1277
0
  JSONWriter jw;
1278
0
  jw << "EditNumber" << edit_num;
1279
1280
0
  if (has_db_id_) {
1281
0
    jw << "DB ID" << db_id_;
1282
0
  }
1283
0
  if (has_comparator_) {
1284
0
    jw << "Comparator" << comparator_;
1285
0
  }
1286
0
  if (has_log_number_) {
1287
0
    jw << "LogNumber" << log_number_;
1288
0
  }
1289
0
  if (has_prev_log_number_) {
1290
0
    jw << "PrevLogNumber" << prev_log_number_;
1291
0
  }
1292
0
  if (has_next_file_number_) {
1293
0
    jw << "NextFileNumber" << next_file_number_;
1294
0
  }
1295
0
  if (has_max_column_family_) {
1296
0
    jw << "MaxColumnFamily" << max_column_family_;
1297
0
  }
1298
0
  if (has_min_log_number_to_keep_) {
1299
0
    jw << "MinLogNumberToKeep" << min_log_number_to_keep_;
1300
0
  }
1301
0
  if (has_last_sequence_) {
1302
0
    jw << "LastSeq" << last_sequence_;
1303
0
  }
1304
1305
0
  if (!deleted_files_.empty()) {
1306
0
    jw << "DeletedFiles";
1307
0
    jw.StartArray();
1308
1309
0
    for (const auto& deleted_file : deleted_files_) {
1310
0
      jw.StartArrayedObject();
1311
0
      jw << "Level" << deleted_file.first;
1312
0
      jw << "FileNumber" << deleted_file.second;
1313
0
      jw.EndArrayedObject();
1314
0
    }
1315
1316
0
    jw.EndArray();
1317
0
  }
1318
1319
0
  if (!new_files_.empty()) {
1320
0
    jw << "AddedFiles";
1321
0
    jw.StartArray();
1322
1323
0
    for (size_t i = 0; i < new_files_.size(); i++) {
1324
0
      jw.StartArrayedObject();
1325
0
      jw << "Level" << new_files_[i].first;
1326
0
      const FileMetaData& f = new_files_[i].second;
1327
0
      jw << "FileNumber" << f.fd.GetNumber();
1328
0
      jw << "FileSize" << f.fd.GetFileSize();
1329
0
      jw << "SmallestIKey" << f.smallest.DebugString(hex_key);
1330
0
      jw << "LargestIKey" << f.largest.DebugString(hex_key);
1331
      // Misspelled key kept for compatibility with tools parsing this output
1332
0
      jw << "OldestAncesterTime" << f.oldest_ancestor_time;
1333
0
      jw << "FileCreationTime" << f.file_creation_time;
1334
0
      jw << "EpochNumber" << f.epoch_number;
1335
0
      if (f.db_ownership_start_time != kUnknownDbOwnershipStartTime) {
1336
0
        jw << "DbOwnershipStartTime" << f.db_ownership_start_time;
1337
0
      }
1338
0
      if (f.data_file_provenance != 0) {
1339
0
        jw << "DataFileProvenance"
1340
0
           << DataFileProvenanceToString(f.data_file_provenance);
1341
0
      }
1342
0
      jw << "FileChecksum" << Slice(f.file_checksum).ToString(true);
1343
0
      jw << "FileChecksumFuncName" << f.file_checksum_func_name;
1344
0
      if (f.temperature != Temperature::kUnknown) {
1345
0
        jw << "temperature" << std::to_string(static_cast<int>(f.temperature));
1346
0
      }
1347
0
      if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {
1348
0
        jw << "OldestBlobFile" << f.oldest_blob_file_number;
1349
0
      }
1350
0
      if (f.temperature != Temperature::kUnknown) {
1351
        // Maybe change to human readable format whenthe feature becomes
1352
        // permanent
1353
0
        jw << "Temperature" << static_cast<int>(f.temperature);
1354
0
      }
1355
0
      jw << "TailSize" << f.tail_size;
1356
0
      jw << "UserDefinedTimestampsPersisted"
1357
0
         << f.user_defined_timestamps_persisted;
1358
0
      if (!f.file_open_metadata.empty()) {
1359
0
        jw << "FileOpenMetadataSize" << f.file_open_metadata.size();
1360
0
      }
1361
0
      jw.EndArrayedObject();
1362
0
    }
1363
1364
0
    jw.EndArray();
1365
0
  }
1366
1367
0
  if (!blob_file_additions_.empty()) {
1368
0
    jw << "BlobFileAdditions";
1369
1370
0
    jw.StartArray();
1371
1372
0
    for (const auto& blob_file_addition : blob_file_additions_) {
1373
0
      jw.StartArrayedObject();
1374
0
      jw << blob_file_addition;
1375
0
      jw.EndArrayedObject();
1376
0
    }
1377
1378
0
    jw.EndArray();
1379
0
  }
1380
1381
0
  if (!blob_file_garbages_.empty()) {
1382
0
    jw << "BlobFileGarbages";
1383
1384
0
    jw.StartArray();
1385
1386
0
    for (const auto& blob_file_garbage : blob_file_garbages_) {
1387
0
      jw.StartArrayedObject();
1388
0
      jw << blob_file_garbage;
1389
0
      jw.EndArrayedObject();
1390
0
    }
1391
1392
0
    jw.EndArray();
1393
0
  }
1394
1395
0
  if (!wal_additions_.empty()) {
1396
0
    jw << "WalAdditions";
1397
1398
0
    jw.StartArray();
1399
1400
0
    for (const auto& wal_addition : wal_additions_) {
1401
0
      jw.StartArrayedObject();
1402
0
      jw << wal_addition;
1403
0
      jw.EndArrayedObject();
1404
0
    }
1405
1406
0
    jw.EndArray();
1407
0
  }
1408
1409
0
  if (!wal_deletion_.IsEmpty()) {
1410
0
    jw << "WalDeletion";
1411
0
    jw.StartObject();
1412
0
    jw << wal_deletion_;
1413
0
    jw.EndObject();
1414
0
  }
1415
1416
0
  jw << "ColumnFamily" << column_family_;
1417
1418
0
  if (is_column_family_add_) {
1419
0
    jw << "ColumnFamilyAdd" << column_family_name_;
1420
0
  }
1421
0
  if (is_column_family_drop_) {
1422
0
    jw << "ColumnFamilyDrop" << column_family_name_;
1423
0
  }
1424
0
  if (is_in_atomic_group_) {
1425
0
    jw << "AtomicGroup" << remaining_entries_;
1426
0
  }
1427
1428
0
  if (HasFullHistoryTsLow()) {
1429
0
    jw << "FullHistoryTsLow" << Slice(full_history_ts_low_).ToString(hex_key);
1430
0
  }
1431
1432
0
  if (HasSubcompactionProgress()) {
1433
0
    jw << "SubcompactionProgress" << subcompaction_progress_.ToString();
1434
0
  }
1435
1436
0
  if (has_last_compacted_manifest_file_size_) {
1437
0
    jw << "LastCompactedManifestFileSize" << last_compacted_manifest_file_size_;
1438
0
  }
1439
1440
0
  if (has_committed_options_file_number_) {
1441
0
    jw << "CommittedOptionsFileNumber" << committed_options_file_number_;
1442
0
  }
1443
1444
0
  if (!dropped_blob_files_.empty()) {
1445
0
    jw << "DroppedBlobFiles";
1446
0
    jw.StartArray();
1447
0
    for (const uint64_t blob_file_number : dropped_blob_files_) {
1448
0
      jw << blob_file_number;
1449
0
    }
1450
0
    jw.EndArray();
1451
0
  }
1452
1453
0
  jw.EndObject();
1454
1455
0
  return jw.Get();
1456
0
}
1457
1458
0
void SubcompactionProgressPerLevel::EncodeTo(std::string* dst) const {
1459
0
  if (num_processed_output_records_ > 0) {
1460
0
    PutVarint32(
1461
0
        dst,
1462
0
        SubcompactionProgressPerLevelCustomTag::kNumProcessedOutputRecords);
1463
0
    std::string varint_records;
1464
0
    PutVarint64(&varint_records, num_processed_output_records_);
1465
0
    PutLengthPrefixedSlice(dst, varint_records);
1466
0
  }
1467
1468
0
  if (!output_files_.empty()) {
1469
0
    PutVarint32(dst, SubcompactionProgressPerLevelCustomTag::kOutputFilesDelta);
1470
0
    std::string files_data;
1471
0
    EncodeOutputFiles(&files_data);
1472
0
    PutLengthPrefixedSlice(dst, files_data);
1473
0
  }
1474
1475
0
  PutVarint32(dst, SubcompactionProgressPerLevelCustomTag::
1476
0
                       kSubcompactionProgressPerLevelTerminate);
1477
0
}
1478
1479
0
Status SubcompactionProgressPerLevel::DecodeFrom(Slice* input) {
1480
0
  Clear();
1481
1482
0
  while (true) {
1483
0
    uint32_t tag = 0;
1484
0
    if (!GetVarint32(input, &tag)) {
1485
0
      return Status::Corruption("SubcompactionProgressPerLevel", "tag error");
1486
0
    }
1487
1488
0
    if (tag == SubcompactionProgressPerLevelCustomTag::
1489
0
                   kSubcompactionProgressPerLevelTerminate) {
1490
0
      break;
1491
0
    }
1492
1493
0
    Slice field;
1494
0
    if (!GetLengthPrefixedSlice(input, &field)) {
1495
0
      return Status::Corruption("SubcompactionProgressPerLevel",
1496
0
                                "field length prefixed slice error");
1497
0
    }
1498
1499
0
    switch (tag) {
1500
0
      case SubcompactionProgressPerLevelCustomTag::kNumProcessedOutputRecords: {
1501
0
        if (!GetVarint64(&field, &num_processed_output_records_)) {
1502
0
          return Status::Corruption("SubcompactionProgressPerLevel",
1503
0
                                    "invalid num_processed_output_records_");
1504
0
        }
1505
0
        break;
1506
0
      }
1507
1508
0
      case SubcompactionProgressPerLevelCustomTag::kOutputFilesDelta: {
1509
0
        Status s = DecodeOutputFiles(&field, output_files_);
1510
0
        if (!s.ok()) {
1511
0
          return s;
1512
0
        }
1513
0
        break;
1514
0
      }
1515
1516
0
      default:
1517
        // Forward compatibility: Handle unknown tags
1518
0
        if ((tag & SubcompactionProgressPerLevelCustomTag::
1519
0
                       kSubcompactionProgressPerLevelCustomTagSafeIgnoreMask) !=
1520
0
            0) {
1521
0
          break;
1522
0
        } else {
1523
0
          return Status::NotSupported("SubcompactionProgress",
1524
0
                                      "unsupported critical custom field");
1525
0
        }
1526
0
    }
1527
0
  }
1528
1529
0
  return Status::OK();
1530
0
}
1531
1532
0
void SubcompactionProgressPerLevel::EncodeOutputFiles(std::string* dst) const {
1533
0
  size_t new_files_count =
1534
0
      output_files_.size() > last_persisted_output_files_count_
1535
0
          ? output_files_.size() - last_persisted_output_files_count_
1536
0
          : 0;
1537
1538
0
  assert(new_files_count > 0);
1539
1540
0
  PutVarint32(dst, static_cast<uint32_t>(new_files_count));
1541
1542
0
  for (size_t i = last_persisted_output_files_count_; i < output_files_.size();
1543
0
       ++i) {
1544
0
    std::string file_dst;
1545
0
    bool ignored_min_log_written = false;
1546
1547
0
    VersionEdit::EncodeToNewFile4(
1548
0
        output_files_[i], -1 /* level */, 0 /* ts_sz */,
1549
0
        false /* has_min_log_number_to_keep */, 0 /* min_log_number_to_keep */,
1550
0
        ignored_min_log_written, &file_dst);
1551
1552
0
    PutLengthPrefixedSlice(dst, file_dst);
1553
0
  }
1554
0
}
1555
1556
Status SubcompactionProgressPerLevel::DecodeOutputFiles(
1557
0
    Slice* input, autovector<FileMetaData>& output_files) {
1558
0
  uint32_t new_file_count = 0;
1559
0
  if (!GetVarint32(input, &new_file_count)) {
1560
0
    return Status::Corruption("SubcompactionProgressPerLevel",
1561
0
                              "new output file count");
1562
0
  }
1563
1564
0
  assert(output_files.size() == 0);
1565
1566
0
  output_files.reserve(new_file_count);
1567
1568
0
  for (uint32_t i = 0; i < new_file_count; ++i) {
1569
0
    Slice file_input;
1570
0
    if (!GetLengthPrefixedSlice(input, &file_input)) {
1571
0
      return Status::Corruption("SubcompactionProgressPerLevel",
1572
0
                                "output file metadata");
1573
0
    }
1574
1575
0
    uint32_t tag = 0;
1576
0
    if (!GetVarint32(&file_input, &tag) || tag != kNewFile4) {
1577
0
      return Status::Corruption("SubcompactionProgressPerLevel",
1578
0
                                "expected kNewFile4 tag");
1579
0
    }
1580
1581
0
    int ignored_max_level = -1;
1582
0
    uint64_t ignored_min_log_number_to_keep = 0;
1583
0
    bool ignored_has_min_log_number_to_keep = false;
1584
0
    VersionEdit::NewFiles ignored_new_files;
1585
0
    FileMetaData file;
1586
1587
0
    const char* err = VersionEdit::DecodeNewFile4From(
1588
0
        &file_input, ignored_max_level, ignored_min_log_number_to_keep,
1589
0
        ignored_has_min_log_number_to_keep, ignored_new_files, file);
1590
1591
0
    if (err != nullptr) {
1592
0
      return Status::Corruption("SubcompactionProgressPerLevel", err);
1593
0
    }
1594
1595
0
    output_files.push_back(std::move(file));
1596
0
  }
1597
1598
0
  return Status::OK();
1599
0
}
1600
1601
0
void SubcompactionProgress::EncodeTo(std::string* dst) const {
1602
0
  if (!next_internal_key_to_compact.empty()) {
1603
0
    PutVarint32(dst, SubcompactionProgressCustomTag::kNextInternalKeyToCompact);
1604
0
    PutLengthPrefixedSlice(dst, next_internal_key_to_compact);
1605
0
  }
1606
1607
0
  PutVarint32(dst, SubcompactionProgressCustomTag::kNumProcessedInputRecords);
1608
0
  std::string varint_records;
1609
0
  PutVarint64(&varint_records, num_processed_input_records);
1610
0
  PutLengthPrefixedSlice(dst, varint_records);
1611
1612
0
  if (output_level_progress.GetOutputFiles().size() >
1613
0
      output_level_progress.GetLastPersistedOutputFilesCount()) {
1614
0
    PutVarint32(dst, SubcompactionProgressCustomTag::kOutputLevelProgress);
1615
0
    std::string level_progress_data;
1616
0
    output_level_progress.EncodeTo(&level_progress_data);
1617
0
    PutLengthPrefixedSlice(dst, level_progress_data);
1618
0
  }
1619
1620
0
  if (proximal_output_level_progress.GetOutputFiles().size() >
1621
0
      proximal_output_level_progress.GetLastPersistedOutputFilesCount()) {
1622
0
    PutVarint32(dst,
1623
0
                SubcompactionProgressCustomTag::kProximalOutputLevelProgress);
1624
0
    std::string level_progress_data;
1625
0
    proximal_output_level_progress.EncodeTo(&level_progress_data);
1626
0
    PutLengthPrefixedSlice(dst, level_progress_data);
1627
0
  }
1628
1629
0
  if (has_blob_compaction_progress) {
1630
0
    PutVarint32(dst, SubcompactionProgressCustomTag::kBlobCompactionProgress);
1631
0
    std::string blob_progress_data;
1632
0
    PutVarint64(&blob_progress_data, next_blob_file_number);
1633
1634
0
    const size_t first_addition = GetLastPersistedBlobFileAdditionsCount();
1635
0
    assert(first_addition <= blob_file_additions.size());
1636
0
    PutVarint64(&blob_progress_data,
1637
0
                blob_file_additions.size() - first_addition);
1638
0
    for (size_t i = first_addition; i < blob_file_additions.size(); ++i) {
1639
0
      std::string encoded_addition;
1640
0
      blob_file_additions[i].EncodeTo(&encoded_addition);
1641
0
      PutLengthPrefixedSlice(&blob_progress_data, encoded_addition);
1642
0
    }
1643
1644
0
    PutVarint64(&blob_progress_data, blob_file_garbages.size());
1645
0
    for (const auto& garbage : blob_file_garbages) {
1646
0
      std::string encoded_garbage;
1647
0
      garbage.EncodeTo(&encoded_garbage);
1648
0
      PutLengthPrefixedSlice(&blob_progress_data, encoded_garbage);
1649
0
    }
1650
0
    PutLengthPrefixedSlice(dst, blob_progress_data);
1651
0
  }
1652
0
  PutVarint32(dst,
1653
0
              SubcompactionProgressCustomTag::kSubcompactionProgressTerminate);
1654
0
}
1655
1656
0
Status SubcompactionProgress::DecodeFrom(Slice* input) {
1657
0
  Clear();
1658
1659
0
  while (true) {
1660
0
    uint32_t custom_tag = 0;
1661
0
    if (!GetVarint32(input, &custom_tag)) {
1662
0
      return Status::Corruption("SubcompactionProgress",
1663
0
                                "custom field tag error");
1664
0
    }
1665
1666
0
    if (custom_tag ==
1667
0
        SubcompactionProgressCustomTag::kSubcompactionProgressTerminate) {
1668
0
      break;
1669
0
    }
1670
1671
0
    Slice field;
1672
0
    if (!GetLengthPrefixedSlice(input, &field)) {
1673
0
      return Status::Corruption("SubcompactionProgress",
1674
0
                                "custom field length prefixed slice error");
1675
0
    }
1676
1677
0
    switch (custom_tag) {
1678
0
      case SubcompactionProgressCustomTag::kNextInternalKeyToCompact:
1679
0
        next_internal_key_to_compact = field.ToString();
1680
0
        break;
1681
1682
0
      case SubcompactionProgressCustomTag::kNumProcessedInputRecords:
1683
0
        if (!GetVarint64(&field, &num_processed_input_records)) {
1684
0
          return Status::Corruption("SubcompactionProgress",
1685
0
                                    "invalid num_processed_input_records");
1686
0
        }
1687
0
        break;
1688
1689
0
      case SubcompactionProgressCustomTag::kOutputLevelProgress: {
1690
0
        Status s = output_level_progress.DecodeFrom(&field);
1691
0
        if (!s.ok()) {
1692
0
          return s;
1693
0
        }
1694
0
        break;
1695
0
      }
1696
1697
0
      case SubcompactionProgressCustomTag::kProximalOutputLevelProgress: {
1698
0
        Status s = proximal_output_level_progress.DecodeFrom(&field);
1699
0
        if (!s.ok()) {
1700
0
          return s;
1701
0
        }
1702
0
        break;
1703
0
      }
1704
1705
0
      case SubcompactionProgressCustomTag::kBlobCompactionProgress: {
1706
0
        has_blob_compaction_progress = true;
1707
0
        if (!GetVarint64(&field, &next_blob_file_number)) {
1708
0
          return Status::Corruption("SubcompactionProgress",
1709
0
                                    "invalid next blob file number");
1710
0
        }
1711
1712
0
        uint64_t num_additions = 0;
1713
0
        if (!GetVarint64(&field, &num_additions) ||
1714
0
            num_additions > field.size()) {
1715
0
          return Status::Corruption("SubcompactionProgress",
1716
0
                                    "invalid blob file addition count");
1717
0
        }
1718
0
        blob_file_additions.reserve(static_cast<size_t>(num_additions));
1719
0
        for (uint64_t i = 0; i < num_additions; ++i) {
1720
0
          Slice encoded_addition;
1721
0
          if (!GetLengthPrefixedSlice(&field, &encoded_addition)) {
1722
0
            return Status::Corruption("SubcompactionProgress",
1723
0
                                      "invalid blob file addition");
1724
0
          }
1725
0
          BlobFileAddition addition;
1726
0
          Status s = addition.DecodeFrom(&encoded_addition);
1727
0
          if (!s.ok() || !encoded_addition.empty()) {
1728
0
            return Status::Corruption("SubcompactionProgress",
1729
0
                                      "invalid blob file addition metadata");
1730
0
          }
1731
0
          blob_file_additions.emplace_back(std::move(addition));
1732
0
        }
1733
1734
0
        uint64_t num_garbages = 0;
1735
0
        if (!GetVarint64(&field, &num_garbages) ||
1736
0
            num_garbages > field.size()) {
1737
0
          return Status::Corruption("SubcompactionProgress",
1738
0
                                    "invalid blob file garbage count");
1739
0
        }
1740
0
        blob_file_garbages.reserve(static_cast<size_t>(num_garbages));
1741
0
        for (uint64_t i = 0; i < num_garbages; ++i) {
1742
0
          Slice encoded_garbage;
1743
0
          if (!GetLengthPrefixedSlice(&field, &encoded_garbage)) {
1744
0
            return Status::Corruption("SubcompactionProgress",
1745
0
                                      "invalid blob file garbage");
1746
0
          }
1747
0
          BlobFileGarbage garbage;
1748
0
          Status s = garbage.DecodeFrom(&encoded_garbage);
1749
0
          if (!s.ok() || !encoded_garbage.empty()) {
1750
0
            return Status::Corruption("SubcompactionProgress",
1751
0
                                      "invalid blob file garbage metadata");
1752
0
          }
1753
0
          blob_file_garbages.emplace_back(std::move(garbage));
1754
0
        }
1755
0
        if (!field.empty()) {
1756
0
          return Status::Corruption("SubcompactionProgress",
1757
0
                                    "trailing blob compaction progress data");
1758
0
        }
1759
0
        break;
1760
0
      }
1761
1762
0
      default:
1763
0
        if ((custom_tag & SubcompactionProgressCustomTag::
1764
0
                              kSubcompactionProgressCustomTagSafeIgnoreMask) !=
1765
0
            0) {
1766
0
          break;
1767
0
        } else {
1768
0
          return Status::NotSupported("SubcompactionProgress",
1769
0
                                      "unsupported critical custom field");
1770
0
        }
1771
0
    }
1772
0
  }
1773
1774
0
  return Status::OK();
1775
0
}
1776
1777
0
bool SubcompactionProgressBuilder::ProcessVersionEdit(const VersionEdit& edit) {
1778
0
  if (!edit.HasSubcompactionProgress()) {
1779
0
    return false;
1780
0
  }
1781
1782
0
  const SubcompactionProgress& progress = edit.GetSubcompactionProgress();
1783
1784
0
  MergeDeltaProgress(progress);
1785
1786
0
  has_subcompaction_progress_ = true;
1787
1788
0
  return true;
1789
0
}
1790
1791
void SubcompactionProgressBuilder::MergeDeltaProgress(
1792
0
    const SubcompactionProgress& delta_progress) {
1793
0
  accumulated_subcompaction_progress_.next_internal_key_to_compact =
1794
0
      delta_progress.next_internal_key_to_compact;
1795
1796
0
  accumulated_subcompaction_progress_.num_processed_input_records =
1797
0
      delta_progress.num_processed_input_records;
1798
1799
0
  MaybeMergeDeltaProgressPerLevel(
1800
0
      accumulated_subcompaction_progress_.output_level_progress,
1801
0
      delta_progress.output_level_progress);
1802
1803
0
  MaybeMergeDeltaProgressPerLevel(
1804
0
      accumulated_subcompaction_progress_.proximal_output_level_progress,
1805
0
      delta_progress.proximal_output_level_progress);
1806
1807
0
  if (delta_progress.has_blob_compaction_progress) {
1808
0
    accumulated_subcompaction_progress_.has_blob_compaction_progress = true;
1809
0
    accumulated_subcompaction_progress_.next_blob_file_number =
1810
0
        delta_progress.next_blob_file_number;
1811
0
    accumulated_subcompaction_progress_.blob_file_additions.insert(
1812
0
        accumulated_subcompaction_progress_.blob_file_additions.end(),
1813
0
        delta_progress.blob_file_additions.begin(),
1814
0
        delta_progress.blob_file_additions.end());
1815
0
    accumulated_subcompaction_progress_.blob_file_garbages =
1816
0
        delta_progress.blob_file_garbages;
1817
0
  }
1818
0
}
1819
1820
void SubcompactionProgressBuilder::MaybeMergeDeltaProgressPerLevel(
1821
    SubcompactionProgressPerLevel& accumulated_level_progress,
1822
0
    const SubcompactionProgressPerLevel& delta_level_progress) {
1823
0
  const auto& delta_files = delta_level_progress.GetOutputFiles();
1824
0
  if (delta_files.empty()) {
1825
0
    return;
1826
0
  }
1827
0
  for (const FileMetaData& file : delta_files) {
1828
0
    accumulated_level_progress.AddToOutputFiles(file);  // Stored as copy
1829
0
  }
1830
1831
0
  accumulated_level_progress.SetNumProcessedOutputRecords(
1832
0
      delta_level_progress.GetNumProcessedOutputRecords());
1833
0
}
1834
1835
0
void SubcompactionProgressBuilder::Clear() {
1836
0
  accumulated_subcompaction_progress_.Clear();
1837
0
  has_subcompaction_progress_ = false;
1838
0
}
1839
}  // namespace ROCKSDB_NAMESPACE