YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/tserver/remote_bootstrap_snapshots.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
14
#include "yb/tserver/remote_bootstrap_snapshots.h"
15
16
#include <unordered_set>
17
18
#include "yb/fs/fs_manager.h"
19
20
#include "yb/tablet/tablet_metadata.h"
21
#include "yb/tablet/tablet_peer.h"
22
#include "yb/tablet/tablet_snapshots.h"
23
24
#include "yb/util/result.h"
25
26
namespace yb {
27
namespace tserver {
28
29
namespace {
30
31
CHECKED_STATUS AddDirToSnapshotFiles(
32
    const std::string& dir, const std::string& prefix, const std::string& snapshot_id,
33
11
    google::protobuf::RepeatedPtrField<tablet::SnapshotFilePB>* out) {
34
11
  auto files = VERIFY_RESULT_PREPEND(
35
11
      Env::Default()->GetChildren(dir, ExcludeDots::kTrue),
36
11
      Format("Unable to list directory $0", dir));
37
38
64
  for (const string& file : files) {
39
64
    LOG(INFO) << "Adding file " << file << " for snapshot " << snapshot_id;
40
64
    const auto path = JoinPathSegments(dir, file);
41
64
    const auto fname = prefix.empty() ? file : JoinPathSegments(prefix, file);
42
43
64
    if (VERIFY_RESULT(Env::Default()->IsDirectory(path))) {
44
0
      RETURN_NOT_OK(AddDirToSnapshotFiles(path, fname, snapshot_id, out));
45
0
      continue;
46
64
    }
47
48
64
    const uint64_t file_size = VERIFY_RESULT_PREPEND(
49
64
        Env::Default()->GetFileSize(path),
50
64
        Format("Unable to get file size for file $0", path));
51
52
64
    auto snapshot_file_pb = out->Add();
53
64
    auto& file_pb = *snapshot_file_pb->mutable_file();
54
64
    snapshot_file_pb->set_snapshot_id(snapshot_id);
55
64
    file_pb.set_name(fname);
56
64
    file_pb.set_size_bytes(file_size);
57
64
    file_pb.set_inode(VERIFY_RESULT(Env::Default()->GetFileINode(path)));
58
64
  }
59
60
11
  return Status::OK();
61
11
}
62
63
} // namespace
64
65
RemoteBootstrapSnapshotsComponent::RemoteBootstrapSnapshotsComponent(
66
    RemoteBootstrapFileDownloader* downloader, tablet::RaftGroupReplicaSuperBlockPB* new_superblock)
67
1.44k
    : downloader_(*downloader), new_superblock_(*new_superblock) {}
68
69
Status RemoteBootstrapSnapshotsComponent::CreateDirectories(
70
1.00k
    const std::string& db_dir, FsManager* fs) {
71
1.00k
  const std::string top_snapshots_dir = tablet::TabletSnapshots::SnapshotsDirName(db_dir);
72
  // Create the snapshots directory.
73
1.00k
  RETURN_NOT_OK_PREPEND(fs->CreateDirIfMissingAndSync(top_snapshots_dir),
74
1.00k
                        Format("Failed to create & sync top snapshots directory $0",
75
1.00k
                               top_snapshots_dir));
76
1.00k
  return Status::OK();
77
1.00k
}
78
79
999
Status RemoteBootstrapSnapshotsComponent::Download() {
80
999
  const auto& kv_store = new_superblock_.kv_store();
81
999
  const string& rocksdb_dir = kv_store.rocksdb_dir();
82
999
  const string top_snapshots_dir = tablet::TabletSnapshots::SnapshotsDirName(rocksdb_dir);
83
  // Create the snapshots directory first.
84
999
  RETURN_NOT_OK_PREPEND(fs_manager().CreateDirIfMissingAndSync(top_snapshots_dir),
85
999
                        Format("Failed to create & sync top snapshots directory $0",
86
999
                               top_snapshots_dir));
87
88
999
  DataIdPB data_id;
89
999
  data_id.set_type(DataIdPB::SNAPSHOT_FILE);
90
  // Used to skip snapshot files from a failed download in a previous iteration of the loop.
91
999
  std::unordered_set<SnapshotId> failed_snapshot_ids;
92
60
  for (auto const& file_pb : kv_store.snapshot_files()) {
93
60
    if (failed_snapshot_ids.find(file_pb.snapshot_id()) != failed_snapshot_ids.end()) {
94
4
      LOG(WARNING) << "Skipping download for file " << file_pb.file().name()
95
4
                   << " because it is part of failed snapshot " << file_pb.snapshot_id();
96
4
      continue;
97
4
    }
98
56
    const string snapshot_dir = JoinPathSegments(top_snapshots_dir, file_pb.snapshot_id());
99
100
56
    RETURN_NOT_OK_PREPEND(fs_manager().CreateDirIfMissingAndSync(snapshot_dir),
101
56
                          Format("Failed to create & sync snapshot directory $0", snapshot_dir));
102
103
56
    const std::string file_path = JoinPathSegments(snapshot_dir, file_pb.file().name());
104
56
    data_id.set_snapshot_id(file_pb.snapshot_id());
105
56
    auto s = downloader_.DownloadFile(file_pb.file(), snapshot_dir, &data_id);
106
56
    if (!s.ok()) {
107
      // If we fail to fetch a snapshot file, delete the snapshot directory, log the error,
108
      // but don't fail the remote bootstrap as snapshot files are not needed for running
109
      // the tablet.
110
2
      LOG(ERROR) << "Error downloading snapshot file " << file_path << ": " << s;
111
2
      failed_snapshot_ids.insert(file_pb.snapshot_id());
112
2
      LOG(INFO) << "Deleting snapshot dir " << snapshot_dir;
113
2
      auto delete_status = Env::Default()->DeleteRecursively(snapshot_dir);
114
2
      if (!delete_status.ok()) {
115
0
        LOG(ERROR) << "Error deleting corrupted snapshot directory "
116
0
                   << snapshot_dir << ": " << delete_status;
117
0
      }
118
54
    } else {
119
54
      LOG(INFO) << "Downloaded file " << file_path << " for snapshot " << file_pb.snapshot_id();
120
54
    }
121
56
  }
122
123
999
  return Status::OK();
124
999
}
125
126
1.44k
Status RemoteBootstrapSnapshotsSource::Init() {
127
1.44k
  const auto& metadata = tablet_peer_->tablet_metadata();
128
1.44k
  auto* kv_store = tablet_superblock_.mutable_kv_store();
129
1.44k
  kv_store->clear_snapshot_files();
130
131
  // Add snapshot files to tablet superblock.
132
1.44k
  const std::string top_snapshots_dir = tablet::TabletSnapshots::SnapshotsDirName(
133
1.44k
      kv_store->rocksdb_dir());
134
1.44k
  std::vector<std::string> snapshots;
135
136
1.44k
  if (metadata->fs_manager()->env()->FileExists(top_snapshots_dir)) {
137
1.31k
    snapshots = VERIFY_RESULT_PREPEND(
138
1.31k
        metadata->fs_manager()->ListDir(top_snapshots_dir),
139
1.31k
        Format("Unable to list directory $0", top_snapshots_dir));
140
1.31k
  }
141
142
1.44k
  for (const string& dir_name : snapshots) {
143
12
    const std::string snapshot_dir = JoinPathSegments(top_snapshots_dir, dir_name);
144
12
    if (tablet::TabletSnapshots::IsTempSnapshotDir(snapshot_dir)) {
145
0
      continue;
146
0
    }
147
148
    // Ignore any non-directories (folder check-sum files, for example).
149
12
    if (!VERIFY_RESULT(Env::Default()->IsDirectory(snapshot_dir))) {
150
1
      continue;
151
1
    }
152
153
11
    RETURN_NOT_OK(AddDirToSnapshotFiles(
154
11
        snapshot_dir, "", dir_name, kv_store->mutable_snapshot_files()));
155
11
  }
156
157
1.44k
  return Status::OK();
158
1.44k
}
159
160
Status RemoteBootstrapSnapshotsSource::GetDataPiece(
161
18
    const DataIdPB& data_id, GetDataPieceInfo* info) {
162
18
  const string snapshots_dir =
163
18
      tablet::TabletSnapshots::SnapshotsDirName(tablet_superblock_.kv_store().rocksdb_dir());
164
18
  return RemoteBootstrapSession::GetFilePiece(
165
18
      JoinPathSegments(snapshots_dir, data_id.snapshot_id()), data_id.file_name(),
166
18
      tablet_peer_->tablet_metadata()->fs_manager()->env(), info);
167
18
}
168
169
18
Status RemoteBootstrapSnapshotsSource::ValidateDataId(const DataIdPB& data_id) {
170
18
  if (data_id.snapshot_id().empty()) {
171
0
    return STATUS(InvalidArgument,
172
0
                  "snapshot id must be specified for type == SNAPSHOT_FILE",
173
0
                  data_id.ShortDebugString());
174
0
  }
175
18
  if (data_id.file_name().empty()) {
176
0
    return STATUS(InvalidArgument,
177
0
                  "file name must be specified for type == SNAPSHOT_FILE",
178
0
                  data_id.ShortDebugString());
179
0
  }
180
18
  return Status::OK();
181
18
}
182
183
} // namespace tserver
184
} // namespace yb