YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/rocksdb/utilities/checkpoint/checkpoint.cc
Line
Count
Source (jump to first uncovered line)
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
//  This source code is licensed under the BSD-style license found in the
3
//  LICENSE file in the root directory of this source tree. An additional grant
4
//  of patent rights can be found in the PATENTS file in the same directory.
5
//
6
// The following only applies to changes made to this file as part of YugaByte development.
7
//
8
// Portions Copyright (c) YugaByte, Inc.
9
//
10
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
11
// in compliance with the License.  You may obtain a copy of the License at
12
//
13
// http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software distributed under the License
16
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17
// or implied.  See the License for the specific language governing permissions and limitations
18
// under the License.
19
//
20
// Copyright (c) 2012 Facebook.
21
// Use of this source code is governed by a BSD-style license that can be
22
// found in the LICENSE file.
23
24
#ifndef ROCKSDB_LITE
25
26
#include "yb/rocksdb/utilities/checkpoint.h"
27
28
#ifndef __STDC_FORMAT_MACROS
29
#define __STDC_FORMAT_MACROS
30
#endif
31
32
#include <inttypes.h>
33
#include <algorithm>
34
#include <string>
35
#include "yb/rocksdb/db/filename.h"
36
#include "yb/rocksdb/db/wal_manager.h"
37
#include "yb/rocksdb/db.h"
38
#include "yb/rocksdb/env.h"
39
#include "yb/rocksdb/transaction_log.h"
40
#include "yb/rocksdb/util/file_util.h"
41
#include "yb/rocksdb/port/port.h"
42
#include "yb/util/random_util.h"
43
#include "yb/util/status_log.h"
44
#include "yb/util/string_util.h"
45
46
namespace rocksdb {
47
namespace checkpoint {
48
49
// Builds an openable snapshot of RocksDB on the same disk, which
50
// accepts an output directory on the same disk, and under the directory
51
// (1) hard-linked SST files pointing to existing live SST files
52
// SST files will be copied if output directory is on a different filesystem
53
// (2) a copied manifest files and other files
54
// The directory should not already exist and will be created by this API.
55
// The directory will be an absolute path
56
1.50k
Status CreateCheckpoint(DB* db, const std::string& checkpoint_dir) {
57
1.50k
  if (!db->GetCheckpointEnv()->IsPlainText()) {
58
0
    return STATUS(InvalidArgument, "db's checkpoint env is not plaintext.");
59
0
  }
60
1.50k
  std::vector<std::string> live_files;
61
1.50k
  uint64_t manifest_file_size = 0;
62
1.50k
  uint64_t sequence_number = db->GetLatestSequenceNumber();
63
1.50k
  bool same_fs = true;
64
1.50k
  VectorLogPtr live_wal_files;
65
1.50k
  bool delete_checkpoint_dir = false;
66
67
1.50k
  Status s = db->GetCheckpointEnv()->FileExists(checkpoint_dir);
68
1.50k
  if (s.ok()) {
69
1
    delete_checkpoint_dir = true;
70
1.50k
  } else if (!s.IsNotFound()) {
71
0
    assert(s.IsIOError());
72
0
    return s;
73
0
  }
74
75
1.50k
  s = db->DisableFileDeletions();
76
1.50k
  if (s.ok()) {
77
    // this will return live_files prefixed with "/"
78
1.50k
    s = db->GetLiveFiles(live_files, &manifest_file_size, true);
79
1.50k
  }
80
  // if we have more than one column family, we need to also get WAL files
81
1.50k
  if (s.ok()) {
82
1.50k
    s = db->GetSortedWalFiles(&live_wal_files);
83
1.50k
  }
84
1.50k
  if (!s.ok()) {
85
0
    WARN_NOT_OK(db->EnableFileDeletions(false), "Failed to disable file deletions");
86
0
    return s;
87
0
  }
88
89
1.50k
  size_t wal_size = live_wal_files.size();
90
1.50k
  RLOG(db->GetOptions().info_log,
91
1.50k
       "Started the snapshot process -- creating snapshot in directory %s",
92
1.50k
       checkpoint_dir.c_str());
93
94
1.50k
  const std::string full_private_path =
95
1.50k
      checkpoint_dir + ".tmp." + ToString(yb::RandomUniformInt<uint64_t>());
96
97
  // create snapshot directory
98
1.50k
  s = db->GetCheckpointEnv()->CreateDir(full_private_path);
99
100
  // copy/hard link live_files
101
7.45k
  for (size_t i = 0; s.ok() && i < live_files.size(); ++i) {
102
5.94k
    uint64_t number;
103
5.94k
    FileType type;
104
5.94k
    bool ok = ParseFileName(live_files[i], &number, &type);
105
5.94k
    if (!ok) {
106
0
      s = STATUS(Corruption, "Can't parse file name. This is very bad");
107
0
      break;
108
0
    }
109
    // we should only get sst, manifest and current files here
110
5.94k
    assert(type == kTableFile || type == kTableSBlockFile || type == kDescriptorFile ||
111
5.94k
           type == kCurrentFile);
112
5.94k
    assert(live_files[i].size() > 0 && live_files[i][0] == '/');
113
5.94k
    std::string src_fname = live_files[i];
114
115
    // rules:
116
    // * if it's kTableFile or kTableSBlockFile, then it's shared
117
    // * if it's kDescriptorFile, limit the size to manifest_file_size
118
    // * always copy if cross-device link
119
5.94k
    bool is_table_file = type == kTableFile || type == kTableSBlockFile;
120
5.94k
    if (is_table_file && same_fs) {
121
2.95k
      RLOG(db->GetOptions().info_log, "Hard Linking %s", src_fname.c_str());
122
2.95k
      s = db->GetCheckpointEnv()->LinkFile(db->GetName() + src_fname,
123
2.95k
                                 full_private_path + src_fname);
124
2.95k
      if (s.IsNotSupported()) {
125
0
        same_fs = false;
126
0
        s = Status::OK();
127
0
      }
128
2.95k
    }
129
5.94k
    if (!is_table_file || !same_fs) {
130
2.99k
      RLOG(db->GetOptions().info_log, "Copying %s", src_fname.c_str());
131
2.99k
      std::string dest_name = full_private_path + src_fname;
132
2.99k
      s = CopyFile(db->GetCheckpointEnv(), db->GetName() + src_fname, dest_name,
133
1.49k
                   type == kDescriptorFile ? manifest_file_size : 0);
134
2.99k
    }
135
5.94k
  }
136
1.50k
  RLOG(db->GetOptions().info_log, "Number of log files %" ROCKSDB_PRIszt,
137
1.50k
       live_wal_files.size());
138
139
  // Link WAL files. Copy exact size of last one because it is the only one
140
  // that has changes after the last flush.
141
1.50k
  for (size_t i = 0; s.ok() && i < wal_size; ++i) {
142
3
    if ((live_wal_files[i]->Type() == kAliveLogFile) &&
143
3
        (live_wal_files[i]->StartSequence() >= sequence_number)) {
144
2
      if (i + 1 == wal_size) {
145
2
        RLOG(db->GetOptions().info_log, "Copying %s",
146
2
             live_wal_files[i]->PathName().c_str());
147
2
        s = CopyFile(db->GetCheckpointEnv(),
148
2
                     db->GetOptions().wal_dir + live_wal_files[i]->PathName(),
149
2
                     full_private_path + live_wal_files[i]->PathName(),
150
2
                     live_wal_files[i]->SizeFileBytes());
151
2
        break;
152
2
      }
153
0
      if (same_fs) {
154
        // we only care about live log files
155
0
        RLOG(db->GetOptions().info_log, "Hard Linking %s",
156
0
             live_wal_files[i]->PathName().c_str());
157
0
        s = db->GetCheckpointEnv()->LinkFile(
158
0
             db->GetOptions().wal_dir + live_wal_files[i]->PathName(),
159
0
             full_private_path + live_wal_files[i]->PathName());
160
0
        if (s.IsNotSupported()) {
161
0
          same_fs = false;
162
0
          s = Status::OK();
163
0
        }
164
0
      }
165
0
      if (!same_fs) {
166
0
        RLOG(db->GetOptions().info_log, "Copying %s",
167
0
             live_wal_files[i]->PathName().c_str());
168
0
        s = CopyFile(db->GetCheckpointEnv(),
169
0
                     db->GetOptions().wal_dir + live_wal_files[i]->PathName(),
170
0
                     full_private_path + live_wal_files[i]->PathName(), 0);
171
0
      }
172
0
    }
173
3
  }
174
175
  // we copied all the files, enable file deletions
176
1.50k
  RETURN_NOT_OK(db->EnableFileDeletions(false));
177
178
1.50k
  if (s.ok()) {
179
1.49k
    if (delete_checkpoint_dir) {
180
1
      const Status s_del = DeleteRecursively(db->GetCheckpointEnv(), checkpoint_dir);
181
1
      RLOG(
182
1
          db->GetOptions().info_log, "Deleted dir %s -- %s",
183
1
          checkpoint_dir.c_str(), s_del.ToString().c_str());
184
1
    }
185
186
    // move tmp private backup to real snapshot directory
187
1.49k
    s = db->GetCheckpointEnv()->RenameFile(full_private_path, checkpoint_dir);
188
1.49k
  }
189
1.50k
  if (s.ok()) {
190
1.49k
    unique_ptr<Directory> checkpoint_directory;
191
1.49k
    RETURN_NOT_OK(db->GetCheckpointEnv()->NewDirectory(checkpoint_dir, &checkpoint_directory));
192
1.49k
    if (checkpoint_directory != nullptr) {
193
1.49k
      s = checkpoint_directory->Fsync();
194
1.49k
    }
195
1.49k
  }
196
197
1.50k
  if (!s.ok()) {
198
    // clean all the files we might have created
199
1
    RLOG(db->GetOptions().info_log, "Snapshot failed -- %s",
200
1
         s.ToString().c_str());
201
    // we have to delete the dir and all its children
202
1
    const Status s_del = DeleteRecursively(db->GetCheckpointEnv(), full_private_path);
203
1
    RLOG(
204
1
        db->GetOptions().info_log, "Deleted dir %s -- %s",
205
1
        full_private_path.c_str(), s_del.ToString().c_str());
206
1
    return s;
207
1
  }
208
209
  // here we know that we succeeded and installed the new snapshot
210
1.50k
  RLOG(db->GetOptions().info_log, "Checkpoint DONE. All is good");
211
1.50k
  RLOG(db->GetOptions().info_log, "Checkpoint sequence number: %" PRIu64,
212
1.50k
      sequence_number);
213
214
1.50k
  return s;
215
1.50k
}
216
217
}  // namespace checkpoint
218
}  // namespace rocksdb
219
220
#endif  // ROCKSDB_LITE