| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802 | //  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.//  This source code is licensed under both the GPLv2 (found in the//  COPYING file in the root directory) and Apache 2.0 License//  (found in the LICENSE.Apache file in the root directory).//// Copyright (c) 2011 The LevelDB Authors. All rights reserved.// Use of this source code is governed by a BSD-style license that can be// found in the LICENSE file. See the AUTHORS file for names of contributors.#include "db/version_edit.h"#include "db/blob_index.h"#include "db/version_set.h"#include "logging/event_logger.h"#include "rocksdb/slice.h"#include "test_util/sync_point.h"#include "util/coding.h"#include "util/string_util.h"namespace ROCKSDB_NAMESPACE {// The unknown file checksum.const std::string kUnknownFileChecksum("");// The unknown sst file checksum function name.const std::string kUnknownFileChecksumFuncName("Unknown");// Mask for an identified tag from the future which can be safely ignored.const uint32_t kTagSafeIgnoreMask = 1 << 13;// Tag numbers for serialized VersionEdit.  These numbers are written to// disk and should not be changed. The number should be forward compatible so// users can down-grade RocksDB safely. A future Tag is ignored by doing '&'// between Tag and kTagSafeIgnoreMask field.enum Tag : uint32_t {  kComparator = 1,  kLogNumber = 2,  kNextFileNumber = 3,  kLastSequence = 4,  kCompactPointer = 5,  kDeletedFile = 6,  kNewFile = 7,  // 8 was used for large value refs  kPrevLogNumber = 9,  kMinLogNumberToKeep = 10,  // Ignore-able field  kDbId = kTagSafeIgnoreMask + 1,  // these are new formats divergent from open source leveldb  kNewFile2 = 100,  kNewFile3 = 102,  kNewFile4 = 103,      // 4th (the latest) format version of adding files  kColumnFamily = 200,  // specify column family for version edit  kColumnFamilyAdd = 201,  kColumnFamilyDrop = 202,  kMaxColumnFamily = 203,  kInAtomicGroup = 300,};enum CustomTag : uint32_t {  kTerminate = 1,  // The end of customized fields  kNeedCompaction = 2,  // Since Manifest is not entirely currently forward-compatible, and the only  // forward-compatible part is the CutsomtTag of kNewFile, we currently encode  // kMinLogNumberToKeep as part of a CustomTag as a hack. This should be  // removed when manifest becomes forward-comptabile.  kMinLogNumberToKeepHack = 3,  kOldestBlobFileNumber = 4,  kOldestAncesterTime = 5,  kFileCreationTime = 6,  kFileChecksum = 7,  kFileChecksumFuncName = 8,  kPathId = 65,};// If this bit for the custom tag is set, opening DB should fail if// we don't know this field.uint32_t kCustomTagNonSafeIgnoreMask = 1 << 6;uint64_t PackFileNumberAndPathId(uint64_t number, uint64_t path_id) {  assert(number <= kFileNumberMask);  return number | (path_id * (kFileNumberMask + 1));}void FileMetaData::UpdateBoundaries(const Slice& key, const Slice& value,                                    SequenceNumber seqno,                                    ValueType value_type) {  if (smallest.size() == 0) {    smallest.DecodeFrom(key);  }  largest.DecodeFrom(key);  fd.smallest_seqno = std::min(fd.smallest_seqno, seqno);  fd.largest_seqno = std::max(fd.largest_seqno, seqno);#ifndef ROCKSDB_LITE  if (value_type == kTypeBlobIndex) {    BlobIndex blob_index;    const Status s = blob_index.DecodeFrom(value);    if (!s.ok()) {      return;    }    if (blob_index.IsInlined()) {      return;    }    if (blob_index.HasTTL()) {      return;    }    // Paranoid check: this should not happen because BlobDB numbers the blob    // files starting from 1.    if (blob_index.file_number() == kInvalidBlobFileNumber) {      return;    }    if (oldest_blob_file_number == kInvalidBlobFileNumber ||        oldest_blob_file_number > blob_index.file_number()) {      oldest_blob_file_number = blob_index.file_number();    }  }#else  (void)value;  (void)value_type;#endif}void VersionEdit::Clear() {  max_level_ = 0;  db_id_.clear();  comparator_.clear();  log_number_ = 0;  prev_log_number_ = 0;  next_file_number_ = 0;  max_column_family_ = 0;  min_log_number_to_keep_ = 0;  last_sequence_ = 0;  has_db_id_ = false;  has_comparator_ = false;  has_log_number_ = false;  has_prev_log_number_ = false;  has_next_file_number_ = false;  has_max_column_family_ = false;  has_min_log_number_to_keep_ = false;  has_last_sequence_ = false;  deleted_files_.clear();  new_files_.clear();  column_family_ = 0;  is_column_family_add_ = false;  is_column_family_drop_ = false;  column_family_name_.clear();  is_in_atomic_group_ = false;  remaining_entries_ = 0;}bool VersionEdit::EncodeTo(std::string* dst) const {  if (has_db_id_) {    PutVarint32(dst, kDbId);    PutLengthPrefixedSlice(dst, db_id_);  }  if (has_comparator_) {    PutVarint32(dst, kComparator);    PutLengthPrefixedSlice(dst, comparator_);  }  if (has_log_number_) {    PutVarint32Varint64(dst, kLogNumber, log_number_);  }  if (has_prev_log_number_) {    PutVarint32Varint64(dst, kPrevLogNumber, prev_log_number_);  }  if (has_next_file_number_) {    PutVarint32Varint64(dst, kNextFileNumber, next_file_number_);  }  if (has_max_column_family_) {    PutVarint32Varint32(dst, kMaxColumnFamily, max_column_family_);  }  if (has_last_sequence_) {    PutVarint32Varint64(dst, kLastSequence, last_sequence_);  }  for (const auto& deleted : deleted_files_) {    PutVarint32Varint32Varint64(dst, kDeletedFile, deleted.first /* level */,                                deleted.second /* file number */);  }  bool min_log_num_written = false;  for (size_t i = 0; i < new_files_.size(); i++) {    const FileMetaData& f = new_files_[i].second;    if (!f.smallest.Valid() || !f.largest.Valid()) {      return false;    }    PutVarint32(dst, kNewFile4);    PutVarint32Varint64(dst, new_files_[i].first /* level */, f.fd.GetNumber());    PutVarint64(dst, f.fd.GetFileSize());    PutLengthPrefixedSlice(dst, f.smallest.Encode());    PutLengthPrefixedSlice(dst, f.largest.Encode());    PutVarint64Varint64(dst, f.fd.smallest_seqno, f.fd.largest_seqno);    // Customized fields' format:    // +-----------------------------+    // | 1st field's tag (varint32)  |    // +-----------------------------+    // | 1st field's size (varint32) |    // +-----------------------------+    // |    bytes for 1st field      |    // |  (based on size decoded)    |    // +-----------------------------+    // |                             |    // |          ......             |    // |                             |    // +-----------------------------+    // | last field's size (varint32)|    // +-----------------------------+    // |    bytes for last field     |    // |  (based on size decoded)    |    // +-----------------------------+    // | terminating tag (varint32)  |    // +-----------------------------+    //    // Customized encoding for fields:    //   tag kPathId: 1 byte as path_id    //   tag kNeedCompaction:    //        now only can take one char value 1 indicating need-compaction    //    PutVarint32(dst, CustomTag::kOldestAncesterTime);    std::string varint_oldest_ancester_time;    PutVarint64(&varint_oldest_ancester_time, f.oldest_ancester_time);    TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:VarintOldestAncesterTime",                             &varint_oldest_ancester_time);    PutLengthPrefixedSlice(dst, Slice(varint_oldest_ancester_time));    PutVarint32(dst, CustomTag::kFileCreationTime);    std::string varint_file_creation_time;    PutVarint64(&varint_file_creation_time, f.file_creation_time);    TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:VarintFileCreationTime",                             &varint_file_creation_time);    PutLengthPrefixedSlice(dst, Slice(varint_file_creation_time));    PutVarint32(dst, CustomTag::kFileChecksum);    PutLengthPrefixedSlice(dst, Slice(f.file_checksum));    PutVarint32(dst, CustomTag::kFileChecksumFuncName);    PutLengthPrefixedSlice(dst, Slice(f.file_checksum_func_name));    if (f.fd.GetPathId() != 0) {      PutVarint32(dst, CustomTag::kPathId);      char p = static_cast<char>(f.fd.GetPathId());      PutLengthPrefixedSlice(dst, Slice(&p, 1));    }    if (f.marked_for_compaction) {      PutVarint32(dst, CustomTag::kNeedCompaction);      char p = static_cast<char>(1);      PutLengthPrefixedSlice(dst, Slice(&p, 1));    }    if (has_min_log_number_to_keep_ && !min_log_num_written) {      PutVarint32(dst, CustomTag::kMinLogNumberToKeepHack);      std::string varint_log_number;      PutFixed64(&varint_log_number, min_log_number_to_keep_);      PutLengthPrefixedSlice(dst, Slice(varint_log_number));      min_log_num_written = true;    }    if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {      PutVarint32(dst, CustomTag::kOldestBlobFileNumber);      std::string oldest_blob_file_number;      PutVarint64(&oldest_blob_file_number, f.oldest_blob_file_number);      PutLengthPrefixedSlice(dst, Slice(oldest_blob_file_number));    }    TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:NewFile4:CustomizeFields",                             dst);    PutVarint32(dst, CustomTag::kTerminate);  }  // 0 is default and does not need to be explicitly written  if (column_family_ != 0) {    PutVarint32Varint32(dst, kColumnFamily, column_family_);  }  if (is_column_family_add_) {    PutVarint32(dst, kColumnFamilyAdd);    PutLengthPrefixedSlice(dst, Slice(column_family_name_));  }  if (is_column_family_drop_) {    PutVarint32(dst, kColumnFamilyDrop);  }  if (is_in_atomic_group_) {    PutVarint32(dst, kInAtomicGroup);    PutVarint32(dst, remaining_entries_);  }  return true;}static bool GetInternalKey(Slice* input, InternalKey* dst) {  Slice str;  if (GetLengthPrefixedSlice(input, &str)) {    dst->DecodeFrom(str);    return dst->Valid();  } else {    return false;  }}bool VersionEdit::GetLevel(Slice* input, int* level, const char** /*msg*/) {  uint32_t v = 0;  if (GetVarint32(input, &v)) {    *level = v;    if (max_level_ < *level) {      max_level_ = *level;    }    return true;  } else {    return false;  }}const char* VersionEdit::DecodeNewFile4From(Slice* input) {  const char* msg = nullptr;  int level = 0;  FileMetaData f;  uint64_t number = 0;  uint32_t path_id = 0;  uint64_t file_size = 0;  SequenceNumber smallest_seqno = 0;  SequenceNumber largest_seqno = kMaxSequenceNumber;  // Since this is the only forward-compatible part of the code, we hack new  // extension into this record. When we do, we set this boolean to distinguish  // the record from the normal NewFile records.  if (GetLevel(input, &level, &msg) && GetVarint64(input, &number) &&      GetVarint64(input, &file_size) && GetInternalKey(input, &f.smallest) &&      GetInternalKey(input, &f.largest) &&      GetVarint64(input, &smallest_seqno) &&      GetVarint64(input, &largest_seqno)) {    // See comments in VersionEdit::EncodeTo() for format of customized fields    while (true) {      uint32_t custom_tag = 0;      Slice field;      if (!GetVarint32(input, &custom_tag)) {        return "new-file4 custom field";      }      if (custom_tag == kTerminate) {        break;      }      if (!GetLengthPrefixedSlice(input, &field)) {        return "new-file4 custom field length prefixed slice error";      }      switch (custom_tag) {        case kPathId:          if (field.size() != 1) {            return "path_id field wrong size";          }          path_id = field[0];          if (path_id > 3) {            return "path_id wrong vaue";          }          break;        case kOldestAncesterTime:          if (!GetVarint64(&field, &f.oldest_ancester_time)) {            return "invalid oldest ancester time";          }          break;        case kFileCreationTime:          if (!GetVarint64(&field, &f.file_creation_time)) {            return "invalid file creation time";          }          break;        case kFileChecksum:          f.file_checksum = field.ToString();          break;        case kFileChecksumFuncName:          f.file_checksum_func_name = field.ToString();          break;        case kNeedCompaction:          if (field.size() != 1) {            return "need_compaction field wrong size";          }          f.marked_for_compaction = (field[0] == 1);          break;        case kMinLogNumberToKeepHack:          // This is a hack to encode kMinLogNumberToKeep in a          // forward-compatible fashion.          if (!GetFixed64(&field, &min_log_number_to_keep_)) {            return "deleted log number malformatted";          }          has_min_log_number_to_keep_ = true;          break;        case kOldestBlobFileNumber:          if (!GetVarint64(&field, &f.oldest_blob_file_number)) {            return "invalid oldest blob file number";          }          break;        default:          if ((custom_tag & kCustomTagNonSafeIgnoreMask) != 0) {            // Should not proceed if cannot understand it            return "new-file4 custom field not supported";          }          break;      }    }  } else {    return "new-file4 entry";  }  f.fd =      FileDescriptor(number, path_id, file_size, smallest_seqno, largest_seqno);  new_files_.push_back(std::make_pair(level, f));  return nullptr;}Status VersionEdit::DecodeFrom(const Slice& src) {  Clear();  Slice input = src;  const char* msg = nullptr;  uint32_t tag = 0;  // Temporary storage for parsing  int level = 0;  FileMetaData f;  Slice str;  InternalKey key;  while (msg == nullptr && GetVarint32(&input, &tag)) {    switch (tag) {      case kDbId:        if (GetLengthPrefixedSlice(&input, &str)) {          db_id_ = str.ToString();          has_db_id_ = true;        } else {          msg = "db id";        }        break;      case kComparator:        if (GetLengthPrefixedSlice(&input, &str)) {          comparator_ = str.ToString();          has_comparator_ = true;        } else {          msg = "comparator name";        }        break;      case kLogNumber:        if (GetVarint64(&input, &log_number_)) {          has_log_number_ = true;        } else {          msg = "log number";        }        break;      case kPrevLogNumber:        if (GetVarint64(&input, &prev_log_number_)) {          has_prev_log_number_ = true;        } else {          msg = "previous log number";        }        break;      case kNextFileNumber:        if (GetVarint64(&input, &next_file_number_)) {          has_next_file_number_ = true;        } else {          msg = "next file number";        }        break;      case kMaxColumnFamily:        if (GetVarint32(&input, &max_column_family_)) {          has_max_column_family_ = true;        } else {          msg = "max column family";        }        break;      case kMinLogNumberToKeep:        if (GetVarint64(&input, &min_log_number_to_keep_)) {          has_min_log_number_to_keep_ = true;        } else {          msg = "min log number to kee";        }        break;      case kLastSequence:        if (GetVarint64(&input, &last_sequence_)) {          has_last_sequence_ = true;        } else {          msg = "last sequence number";        }        break;      case kCompactPointer:        if (GetLevel(&input, &level, &msg) &&            GetInternalKey(&input, &key)) {          // we don't use compact pointers anymore,          // but we should not fail if they are still          // in manifest        } else {          if (!msg) {            msg = "compaction pointer";          }        }        break;      case kDeletedFile: {        uint64_t number = 0;        if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number)) {          deleted_files_.insert(std::make_pair(level, number));        } else {          if (!msg) {            msg = "deleted file";          }        }        break;      }      case kNewFile: {        uint64_t number = 0;        uint64_t file_size = 0;        if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&            GetVarint64(&input, &file_size) &&            GetInternalKey(&input, &f.smallest) &&            GetInternalKey(&input, &f.largest)) {          f.fd = FileDescriptor(number, 0, file_size);          new_files_.push_back(std::make_pair(level, f));        } else {          if (!msg) {            msg = "new-file entry";          }        }        break;      }      case kNewFile2: {        uint64_t number = 0;        uint64_t file_size = 0;        SequenceNumber smallest_seqno = 0;        SequenceNumber largest_seqno = kMaxSequenceNumber;        if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&            GetVarint64(&input, &file_size) &&            GetInternalKey(&input, &f.smallest) &&            GetInternalKey(&input, &f.largest) &&            GetVarint64(&input, &smallest_seqno) &&            GetVarint64(&input, &largest_seqno)) {          f.fd = FileDescriptor(number, 0, file_size, smallest_seqno,                                largest_seqno);          new_files_.push_back(std::make_pair(level, f));        } else {          if (!msg) {            msg = "new-file2 entry";          }        }        break;      }      case kNewFile3: {        uint64_t number = 0;        uint32_t path_id = 0;        uint64_t file_size = 0;        SequenceNumber smallest_seqno = 0;        SequenceNumber largest_seqno = kMaxSequenceNumber;        if (GetLevel(&input, &level, &msg) && GetVarint64(&input, &number) &&            GetVarint32(&input, &path_id) && GetVarint64(&input, &file_size) &&            GetInternalKey(&input, &f.smallest) &&            GetInternalKey(&input, &f.largest) &&            GetVarint64(&input, &smallest_seqno) &&            GetVarint64(&input, &largest_seqno)) {          f.fd = FileDescriptor(number, path_id, file_size, smallest_seqno,                                largest_seqno);          new_files_.push_back(std::make_pair(level, f));        } else {          if (!msg) {            msg = "new-file3 entry";          }        }        break;      }      case kNewFile4: {        msg = DecodeNewFile4From(&input);        break;      }      case kColumnFamily:        if (!GetVarint32(&input, &column_family_)) {          if (!msg) {            msg = "set column family id";          }        }        break;      case kColumnFamilyAdd:        if (GetLengthPrefixedSlice(&input, &str)) {          is_column_family_add_ = true;          column_family_name_ = str.ToString();        } else {          if (!msg) {            msg = "column family add";          }        }        break;      case kColumnFamilyDrop:        is_column_family_drop_ = true;        break;      case kInAtomicGroup:        is_in_atomic_group_ = true;        if (!GetVarint32(&input, &remaining_entries_)) {          if (!msg) {            msg = "remaining entries";          }        }        break;      default:        if (tag & kTagSafeIgnoreMask) {          // Tag from future which can be safely ignored.          // The next field must be the length of the entry.          uint32_t field_len;          if (!GetVarint32(&input, &field_len) ||              static_cast<size_t>(field_len) > input.size()) {            if (!msg) {              msg = "safely ignoreable tag length error";            }          } else {            input.remove_prefix(static_cast<size_t>(field_len));          }        } else {          msg = "unknown tag";        }        break;    }  }  if (msg == nullptr && !input.empty()) {    msg = "invalid tag";  }  Status result;  if (msg != nullptr) {    result = Status::Corruption("VersionEdit", msg);  }  return result;}std::string VersionEdit::DebugString(bool hex_key) const {  std::string r;  r.append("VersionEdit {");  if (has_db_id_) {    r.append("\n  DB ID: ");    r.append(db_id_);  }  if (has_comparator_) {    r.append("\n  Comparator: ");    r.append(comparator_);  }  if (has_log_number_) {    r.append("\n  LogNumber: ");    AppendNumberTo(&r, log_number_);  }  if (has_prev_log_number_) {    r.append("\n  PrevLogNumber: ");    AppendNumberTo(&r, prev_log_number_);  }  if (has_next_file_number_) {    r.append("\n  NextFileNumber: ");    AppendNumberTo(&r, next_file_number_);  }  if (has_max_column_family_) {    r.append("\n  MaxColumnFamily: ");    AppendNumberTo(&r, max_column_family_);  }  if (has_min_log_number_to_keep_) {    r.append("\n  MinLogNumberToKeep: ");    AppendNumberTo(&r, min_log_number_to_keep_);  }  if (has_last_sequence_) {    r.append("\n  LastSeq: ");    AppendNumberTo(&r, last_sequence_);  }  for (const auto& deleted_file : deleted_files_) {    r.append("\n  DeleteFile: ");    AppendNumberTo(&r, deleted_file.first);    r.append(" ");    AppendNumberTo(&r, deleted_file.second);  }  for (size_t i = 0; i < new_files_.size(); i++) {    const FileMetaData& f = new_files_[i].second;    r.append("\n  AddFile: ");    AppendNumberTo(&r, new_files_[i].first);    r.append(" ");    AppendNumberTo(&r, f.fd.GetNumber());    r.append(" ");    AppendNumberTo(&r, f.fd.GetFileSize());    r.append(" ");    r.append(f.smallest.DebugString(hex_key));    r.append(" .. ");    r.append(f.largest.DebugString(hex_key));    if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {      r.append(" blob_file:");      AppendNumberTo(&r, f.oldest_blob_file_number);    }    r.append(" oldest_ancester_time:");    AppendNumberTo(&r, f.oldest_ancester_time);    r.append(" file_creation_time:");    AppendNumberTo(&r, f.file_creation_time);    r.append(" file_checksum:");    r.append(f.file_checksum);    r.append(" file_checksum_func_name: ");    r.append(f.file_checksum_func_name);  }  r.append("\n  ColumnFamily: ");  AppendNumberTo(&r, column_family_);  if (is_column_family_add_) {    r.append("\n  ColumnFamilyAdd: ");    r.append(column_family_name_);  }  if (is_column_family_drop_) {    r.append("\n  ColumnFamilyDrop");  }  if (is_in_atomic_group_) {    r.append("\n  AtomicGroup: ");    AppendNumberTo(&r, remaining_entries_);    r.append(" entries remains");  }  r.append("\n}\n");  return r;}std::string VersionEdit::DebugJSON(int edit_num, bool hex_key) const {  JSONWriter jw;  jw << "EditNumber" << edit_num;  if (has_db_id_) {    jw << "DB ID" << db_id_;  }  if (has_comparator_) {    jw << "Comparator" << comparator_;  }  if (has_log_number_) {    jw << "LogNumber" << log_number_;  }  if (has_prev_log_number_) {    jw << "PrevLogNumber" << prev_log_number_;  }  if (has_next_file_number_) {    jw << "NextFileNumber" << next_file_number_;  }  if (has_max_column_family_) {    jw << "MaxColumnFamily" << max_column_family_;  }  if (has_min_log_number_to_keep_) {    jw << "MinLogNumberToKeep" << min_log_number_to_keep_;  }  if (has_last_sequence_) {    jw << "LastSeq" << last_sequence_;  }  if (!deleted_files_.empty()) {    jw << "DeletedFiles";    jw.StartArray();    for (const auto& deleted_file : deleted_files_) {      jw.StartArrayedObject();      jw << "Level" << deleted_file.first;      jw << "FileNumber" << deleted_file.second;      jw.EndArrayedObject();    }    jw.EndArray();  }  if (!new_files_.empty()) {    jw << "AddedFiles";    jw.StartArray();    for (size_t i = 0; i < new_files_.size(); i++) {      jw.StartArrayedObject();      jw << "Level" << new_files_[i].first;      const FileMetaData& f = new_files_[i].second;      jw << "FileNumber" << f.fd.GetNumber();      jw << "FileSize" << f.fd.GetFileSize();      jw << "SmallestIKey" << f.smallest.DebugString(hex_key);      jw << "LargestIKey" << f.largest.DebugString(hex_key);      if (f.oldest_blob_file_number != kInvalidBlobFileNumber) {        jw << "OldestBlobFile" << f.oldest_blob_file_number;      }      jw.EndArrayedObject();    }    jw.EndArray();  }  jw << "ColumnFamily" << column_family_;  if (is_column_family_add_) {    jw << "ColumnFamilyAdd" << column_family_name_;  }  if (is_column_family_drop_) {    jw << "ColumnFamilyDrop" << column_family_name_;  }  if (is_in_atomic_group_) {    jw << "AtomicGroup" << remaining_entries_;  }  jw.EndObject();  return jw.Get();}}  // namespace ROCKSDB_NAMESPACE
 |