/Users/deen/code/yugabyte-db/src/yb/rocksdb/utilities/env_mirror.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2015, Red Hat, 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 | | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
6 | | // Use of this source code is governed by a BSD-style license that can be |
7 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
8 | | // |
9 | | // The following only applies to changes made to this file as part of YugaByte development. |
10 | | // |
11 | | // Portions Copyright (c) YugaByte, Inc. |
12 | | // |
13 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
14 | | // in compliance with the License. You may obtain a copy of the License at |
15 | | // |
16 | | // http://www.apache.org/licenses/LICENSE-2.0 |
17 | | // |
18 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
19 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
20 | | // or implied. See the License for the specific language governing permissions and limitations |
21 | | // under the License. |
22 | | // |
23 | | |
24 | | #ifndef ROCKSDB_LITE |
25 | | |
26 | | #include <glog/logging.h> |
27 | | |
28 | | #include "yb/rocksdb/utilities/env_mirror.h" |
29 | | #include "yb/util/result.h" |
30 | | #include "yb/util/status_format.h" |
31 | | |
32 | | namespace rocksdb { |
33 | | |
34 | | // An implementaiton of Env that mirrors all work over two backend |
35 | | // Env's. This is useful for debugging purposes. |
36 | | class SequentialFileMirror : public SequentialFile { |
37 | | public: |
38 | | unique_ptr<SequentialFile> a_, b_; |
39 | | std::string fname; |
40 | 0 | explicit SequentialFileMirror(std::string f) : fname(std::move(f)) {} |
41 | | |
42 | 0 | Status Read(size_t n, Slice* result, uint8_t* scratch) override { |
43 | 0 | Slice aslice; |
44 | 0 | Status as = a_->Read(n, &aslice, scratch); |
45 | 0 | if (as.ok()) { |
46 | 0 | std::unique_ptr<uint8_t[]> bscratch(new uint8_t[n]); |
47 | 0 | Slice bslice; |
48 | 0 | size_t off = 0; |
49 | 0 | size_t left = aslice.size(); |
50 | 0 | while (left) { |
51 | 0 | Status bs = b_->Read(left, &bslice, bscratch.get()); |
52 | 0 | assert(as.code() == bs.code()); |
53 | 0 | assert(memcmp(bscratch.get(), scratch + off, bslice.size()) == 0); |
54 | 0 | off += bslice.size(); |
55 | 0 | left -= bslice.size(); |
56 | 0 | } |
57 | 0 | *result = aslice; |
58 | 0 | } else { |
59 | 0 | Status bs = b_->Read(n, result, scratch); |
60 | 0 | assert(as.code() == bs.code()); |
61 | 0 | } |
62 | 0 | return as; |
63 | 0 | } |
64 | | |
65 | 0 | Status Skip(uint64_t n) override { |
66 | 0 | Status as = a_->Skip(n); |
67 | 0 | Status bs = b_->Skip(n); |
68 | 0 | assert(as.code() == bs.code()); |
69 | 0 | return as; |
70 | 0 | } |
71 | | |
72 | 0 | Status InvalidateCache(size_t offset, size_t length) override { |
73 | 0 | Status as = a_->InvalidateCache(offset, length); |
74 | 0 | Status bs = b_->InvalidateCache(offset, length); |
75 | 0 | assert(as.code() == bs.code()); |
76 | 0 | return as; |
77 | 0 | } |
78 | | |
79 | 0 | const std::string& filename() const override { return fname; } |
80 | | }; |
81 | | |
82 | | class RandomAccessFileMirror : public RandomAccessFile { |
83 | | public: |
84 | | unique_ptr<RandomAccessFile> a_, b_; |
85 | | std::string fname; |
86 | 0 | explicit RandomAccessFileMirror(std::string f) : fname(std::move(f)) {} |
87 | | |
88 | 0 | Status Read(uint64_t offset, size_t n, Slice* result, uint8_t* scratch) const override { |
89 | 0 | Status as = a_->Read(offset, n, result, scratch); |
90 | 0 | if (as.ok()) { |
91 | 0 | uint8_t* bscratch = new uint8_t[n]; |
92 | 0 | Slice bslice; |
93 | 0 | size_t off = 0; |
94 | 0 | size_t left = result->size(); |
95 | 0 | while (left) { |
96 | 0 | Status bs = b_->Read(offset + off, left, &bslice, bscratch); |
97 | 0 | assert(as.code() == bs.code()); |
98 | 0 | assert(memcmp(bscratch, scratch + off, bslice.size()) == 0); |
99 | 0 | off += bslice.size(); |
100 | 0 | left -= bslice.size(); |
101 | 0 | } |
102 | 0 | delete[] bscratch; |
103 | 0 | } else { |
104 | 0 | Status bs = b_->Read(offset, n, result, scratch); |
105 | 0 | assert(as.code() == bs.code()); |
106 | 0 | } |
107 | 0 | return as; |
108 | 0 | } |
109 | | |
110 | 0 | yb::Result<uint64_t> Size() const override { |
111 | 0 | const auto a_size = a_->Size(); |
112 | 0 | const auto b_size = b_->Size(); |
113 | 0 | CHECK_EQ(a_size.ok(), b_size.ok()); |
114 | 0 | if (a_size.ok()) { |
115 | 0 | CHECK_EQ(*a_size, *b_size); |
116 | 0 | } |
117 | 0 | return a_size; |
118 | 0 | } |
119 | | |
120 | 0 | yb::Result<uint64_t> INode() const override { |
121 | 0 | return STATUS(NotSupported, "INode", "Not supported in RandomAccessFileMirror"); |
122 | 0 | } |
123 | | |
124 | 0 | bool ShouldForwardRawRequest() const override { |
125 | | // NOTE: not verified |
126 | 0 | return a_->ShouldForwardRawRequest(); |
127 | 0 | } |
128 | | |
129 | 0 | size_t GetUniqueId(char* id) const override { |
130 | | // NOTE: not verified |
131 | 0 | return a_->GetUniqueId(id); |
132 | 0 | } |
133 | | |
134 | 0 | size_t memory_footprint() const override { |
135 | 0 | LOG(FATAL) << "memory_footprint is not supported in RandomAccessFileMirror"; |
136 | 0 | } |
137 | | |
138 | 0 | const std::string& filename() const override { return fname; } |
139 | | }; |
140 | | |
141 | | class WritableFileMirror : public WritableFile { |
142 | | public: |
143 | | unique_ptr<WritableFile> a_, b_; |
144 | | std::string fname; |
145 | 0 | explicit WritableFileMirror(std::string f) : fname(std::move(f)) {} |
146 | | |
147 | 0 | Status Append(const Slice& data) override { |
148 | 0 | Status as = a_->Append(data); |
149 | 0 | Status bs = b_->Append(data); |
150 | 0 | assert(as.code() == bs.code()); |
151 | 0 | return as; |
152 | 0 | } |
153 | 0 | Status PositionedAppend(const Slice& data, uint64_t offset) override { |
154 | 0 | Status as = a_->PositionedAppend(data, offset); |
155 | 0 | Status bs = b_->PositionedAppend(data, offset); |
156 | 0 | assert(as.code() == bs.code()); |
157 | 0 | return as; |
158 | 0 | } |
159 | 0 | Status Truncate(uint64_t size) override { |
160 | 0 | Status as = a_->Truncate(size); |
161 | 0 | Status bs = b_->Truncate(size); |
162 | 0 | assert(as.code() == bs.code()); |
163 | 0 | return as; |
164 | 0 | } |
165 | 0 | Status Close() override { |
166 | 0 | Status as = a_->Close(); |
167 | 0 | Status bs = b_->Close(); |
168 | 0 | assert(as.code() == bs.code()); |
169 | 0 | return as; |
170 | 0 | } |
171 | 0 | Status Flush() override { |
172 | 0 | Status as = a_->Flush(); |
173 | 0 | Status bs = b_->Flush(); |
174 | 0 | assert(as.code() == bs.code()); |
175 | 0 | return as; |
176 | 0 | } |
177 | 0 | Status Sync() override { |
178 | 0 | Status as = a_->Sync(); |
179 | 0 | Status bs = b_->Sync(); |
180 | 0 | assert(as.code() == bs.code()); |
181 | 0 | return as; |
182 | 0 | } |
183 | 0 | Status Fsync() override { |
184 | 0 | Status as = a_->Fsync(); |
185 | 0 | Status bs = b_->Fsync(); |
186 | 0 | assert(as.code() == bs.code()); |
187 | 0 | return as; |
188 | 0 | } |
189 | 0 | bool IsSyncThreadSafe() const override { |
190 | 0 | bool as = a_->IsSyncThreadSafe(); |
191 | 0 | assert(as == b_->IsSyncThreadSafe()); |
192 | 0 | return as; |
193 | 0 | } |
194 | 0 | void SetIOPriority(Env::IOPriority pri) override { |
195 | 0 | a_->SetIOPriority(pri); |
196 | 0 | b_->SetIOPriority(pri); |
197 | 0 | } |
198 | 0 | Env::IOPriority GetIOPriority() override { |
199 | | // NOTE: we don't verify this one |
200 | 0 | return a_->GetIOPriority(); |
201 | 0 | } |
202 | 0 | uint64_t GetFileSize() override { |
203 | 0 | uint64_t as = a_->GetFileSize(); |
204 | 0 | assert(as == b_->GetFileSize()); |
205 | 0 | return as; |
206 | 0 | } |
207 | | void GetPreallocationStatus(size_t* block_size, |
208 | 0 | size_t* last_allocated_block) override { |
209 | | // NOTE: we don't verify this one |
210 | 0 | return a_->GetPreallocationStatus(block_size, last_allocated_block); |
211 | 0 | } |
212 | 0 | size_t GetUniqueId(char* id) const override { |
213 | | // NOTE: we don't verify this one |
214 | 0 | return a_->GetUniqueId(id); |
215 | 0 | } |
216 | 0 | Status InvalidateCache(size_t offset, size_t length) override { |
217 | 0 | Status as = a_->InvalidateCache(offset, length); |
218 | 0 | Status bs = b_->InvalidateCache(offset, length); |
219 | 0 | assert(as.code() == bs.code()); |
220 | 0 | return as; |
221 | 0 | } |
222 | | |
223 | | protected: |
224 | 0 | Status Allocate(uint64_t offset, uint64_t length) override { |
225 | 0 | Status as = a_->Allocate(offset, length); |
226 | 0 | Status bs = b_->Allocate(offset, length); |
227 | 0 | assert(as.code() == bs.code()); |
228 | 0 | return as; |
229 | 0 | } |
230 | 0 | Status RangeSync(uint64_t offset, uint64_t nbytes) override { |
231 | 0 | Status as = a_->RangeSync(offset, nbytes); |
232 | 0 | Status bs = b_->RangeSync(offset, nbytes); |
233 | 0 | assert(as.code() == bs.code()); |
234 | 0 | return as; |
235 | 0 | } |
236 | | }; |
237 | | |
238 | | Status EnvMirror::NewSequentialFile(const std::string& f, |
239 | | unique_ptr<SequentialFile>* r, |
240 | 0 | const EnvOptions& options) { |
241 | 0 | if (f.find("/proc/") == 0) { |
242 | 0 | return a_->NewSequentialFile(f, r, options); |
243 | 0 | } |
244 | 0 | SequentialFileMirror* mf = new SequentialFileMirror(f); |
245 | 0 | Status as = a_->NewSequentialFile(f, &mf->a_, options); |
246 | 0 | Status bs = b_->NewSequentialFile(f, &mf->b_, options); |
247 | 0 | assert(as.code() == bs.code()); |
248 | 0 | if (as.ok()) |
249 | 0 | r->reset(mf); |
250 | 0 | else |
251 | 0 | delete mf; |
252 | 0 | return as; |
253 | 0 | } |
254 | | |
255 | | Status EnvMirror::NewRandomAccessFile(const std::string& f, |
256 | | unique_ptr<RandomAccessFile>* r, |
257 | 0 | const EnvOptions& options) { |
258 | 0 | if (f.find("/proc/") == 0) { |
259 | 0 | return a_->NewRandomAccessFile(f, r, options); |
260 | 0 | } |
261 | 0 | RandomAccessFileMirror* mf = new RandomAccessFileMirror(f); |
262 | 0 | Status as = a_->NewRandomAccessFile(f, &mf->a_, options); |
263 | 0 | Status bs = b_->NewRandomAccessFile(f, &mf->b_, options); |
264 | 0 | assert(as.code() == bs.code()); |
265 | 0 | if (as.ok()) |
266 | 0 | r->reset(mf); |
267 | 0 | else |
268 | 0 | delete mf; |
269 | 0 | return as; |
270 | 0 | } |
271 | | |
272 | | Status EnvMirror::NewWritableFile(const std::string& f, |
273 | | unique_ptr<WritableFile>* r, |
274 | 0 | const EnvOptions& options) { |
275 | 0 | if (f.find("/proc/") == 0) return a_->NewWritableFile(f, r, options); |
276 | 0 | WritableFileMirror* mf = new WritableFileMirror(f); |
277 | 0 | Status as = a_->NewWritableFile(f, &mf->a_, options); |
278 | 0 | Status bs = b_->NewWritableFile(f, &mf->b_, options); |
279 | 0 | assert(as.code() == bs.code()); |
280 | 0 | if (as.ok()) |
281 | 0 | r->reset(mf); |
282 | 0 | else |
283 | 0 | delete mf; |
284 | 0 | return as; |
285 | 0 | } |
286 | | |
287 | | Status EnvMirror::ReuseWritableFile(const std::string& fname, |
288 | | const std::string& old_fname, |
289 | | unique_ptr<WritableFile>* r, |
290 | 0 | const EnvOptions& options) { |
291 | 0 | if (fname.find("/proc/") == 0) |
292 | 0 | return a_->ReuseWritableFile(fname, old_fname, r, options); |
293 | 0 | WritableFileMirror* mf = new WritableFileMirror(fname); |
294 | 0 | Status as = a_->ReuseWritableFile(fname, old_fname, &mf->a_, options); |
295 | 0 | Status bs = b_->ReuseWritableFile(fname, old_fname, &mf->b_, options); |
296 | 0 | assert(as.code() == bs.code()); |
297 | 0 | if (as.ok()) |
298 | 0 | r->reset(mf); |
299 | 0 | else |
300 | 0 | delete mf; |
301 | 0 | return as; |
302 | 0 | } |
303 | | |
304 | | Status EnvMirror::NewDirectory(const std::string& name, |
305 | 0 | unique_ptr<Directory>* result) { |
306 | 0 | unique_ptr<Directory> br; |
307 | 0 | Status as = a_->NewDirectory(name, result); |
308 | 0 | Status bs = b_->NewDirectory(name, &br); |
309 | 0 | assert(as.code() == bs.code()); |
310 | 0 | return as; |
311 | 0 | } |
312 | | |
313 | 0 | Status EnvMirror::FileExists(const std::string& f) { |
314 | 0 | Status as = a_->FileExists(f); |
315 | 0 | Status bs = b_->FileExists(f); |
316 | 0 | assert(as.code() == bs.code()); |
317 | 0 | return as; |
318 | 0 | } |
319 | | |
320 | | Status EnvMirror::GetChildren(const std::string& dir, |
321 | 0 | std::vector<std::string>* r) { |
322 | 0 | std::vector<std::string> ar, br; |
323 | 0 | Status as = a_->GetChildren(dir, &ar); |
324 | 0 | Status bs = b_->GetChildren(dir, &br); |
325 | 0 | assert(as.code() == bs.code()); |
326 | 0 | std::sort(ar.begin(), ar.end()); |
327 | 0 | std::sort(br.begin(), br.end()); |
328 | 0 | if (!as.ok() || ar != br) { |
329 | 0 | assert(0 == "getchildren results don't match"); |
330 | 0 | } |
331 | 0 | *r = ar; |
332 | 0 | return as; |
333 | 0 | } |
334 | | |
335 | 0 | Status EnvMirror::DeleteFile(const std::string& f) { |
336 | 0 | Status as = a_->DeleteFile(f); |
337 | 0 | Status bs = b_->DeleteFile(f); |
338 | 0 | assert(as.code() == bs.code()); |
339 | 0 | return as; |
340 | 0 | } |
341 | | |
342 | 0 | Status EnvMirror::CreateDir(const std::string& d) { |
343 | 0 | Status as = a_->CreateDir(d); |
344 | 0 | Status bs = b_->CreateDir(d); |
345 | 0 | assert(as.code() == bs.code()); |
346 | 0 | return as; |
347 | 0 | } |
348 | | |
349 | 0 | Status EnvMirror::CreateDirIfMissing(const std::string& d) { |
350 | 0 | Status as = a_->CreateDirIfMissing(d); |
351 | 0 | Status bs = b_->CreateDirIfMissing(d); |
352 | 0 | assert(as.code() == bs.code()); |
353 | 0 | return as; |
354 | 0 | } |
355 | | |
356 | 0 | Status EnvMirror::DeleteDir(const std::string& d) { |
357 | 0 | Status as = a_->DeleteDir(d); |
358 | 0 | Status bs = b_->DeleteDir(d); |
359 | 0 | assert(as.code() == bs.code()); |
360 | 0 | return as; |
361 | 0 | } |
362 | | |
363 | 0 | Status EnvMirror::GetFileSize(const std::string& f, uint64_t* s) { |
364 | 0 | uint64_t asize, bsize; |
365 | 0 | Status as = a_->GetFileSize(f, &asize); |
366 | 0 | Status bs = b_->GetFileSize(f, &bsize); |
367 | 0 | assert(as.code() == bs.code()); |
368 | 0 | assert(!as.ok() || asize == bsize); |
369 | 0 | *s = asize; |
370 | 0 | return as; |
371 | 0 | } |
372 | | |
373 | | Status EnvMirror::GetFileModificationTime(const std::string& fname, |
374 | 0 | uint64_t* file_mtime) { |
375 | 0 | uint64_t amtime, bmtime; |
376 | 0 | Status as = a_->GetFileModificationTime(fname, &amtime); |
377 | 0 | Status bs = b_->GetFileModificationTime(fname, &bmtime); |
378 | 0 | assert(as.code() == bs.code()); |
379 | 0 | assert(!as.ok() || amtime - bmtime < 10000 || bmtime - amtime < 10000); |
380 | 0 | *file_mtime = amtime; |
381 | 0 | return as; |
382 | 0 | } |
383 | | |
384 | 0 | Status EnvMirror::RenameFile(const std::string& s, const std::string& t) { |
385 | 0 | Status as = a_->RenameFile(s, t); |
386 | 0 | Status bs = b_->RenameFile(s, t); |
387 | 0 | assert(as.code() == bs.code()); |
388 | 0 | return as; |
389 | 0 | } |
390 | | |
391 | 0 | Status EnvMirror::LinkFile(const std::string& s, const std::string& t) { |
392 | 0 | Status as = a_->LinkFile(s, t); |
393 | 0 | Status bs = b_->LinkFile(s, t); |
394 | 0 | assert(as.code() == bs.code()); |
395 | 0 | return as; |
396 | 0 | } |
397 | | |
398 | 0 | Status EnvMirror::LockFile(const std::string& f, FileLock** l) { |
399 | 0 | FileLock* al, *bl; |
400 | 0 | Status as = a_->LockFile(f, &al); |
401 | 0 | Status bs = b_->LockFile(f, &bl); |
402 | 0 | assert(as.code() == bs.code()); |
403 | 0 | if (as.ok()) *l = new FileLockMirror(al, bl); |
404 | 0 | return as; |
405 | 0 | } |
406 | | |
407 | 0 | Status EnvMirror::UnlockFile(FileLock* l) { |
408 | 0 | FileLockMirror* ml = static_cast<FileLockMirror*>(l); |
409 | 0 | Status as = a_->UnlockFile(ml->a_); |
410 | 0 | Status bs = b_->UnlockFile(ml->b_); |
411 | 0 | assert(as.code() == bs.code()); |
412 | 0 | return as; |
413 | 0 | } |
414 | | |
415 | | } // namespace rocksdb |
416 | | #endif |