Avoid usage of ReopenWritableFile in db_stress (#9649)

Summary:
The UniqueIdVerifier constructor currently calls ReopenWritableFile on
the FileSystem, which might not be supported. Instead of relying on
reopening the unique IDs file for writing, create a new file and copy
the original contents.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/9649

Test Plan: Run db_stress

Reviewed By: pdillinger

Differential Revision: D34572307

Pulled By: anand1976

fbshipit-source-id: 3a777908582d79dae57488d4278bad126774f698
This commit is contained in:
anand76 2022-03-04 10:30:10 -08:00 committed by Facebook GitHub Bot
parent 67542bfab5
commit 3362a730dc
4 changed files with 87 additions and 23 deletions

View File

@ -7,6 +7,7 @@
#include <cstdint> #include <cstdint>
#include "file/file_util.h"
#include "rocksdb/file_system.h" #include "rocksdb/file_system.h"
#include "util/coding_lean.h" #include "util/coding_lean.h"
@ -37,10 +38,31 @@ UniqueIdVerifier::UniqueIdVerifier(const std::string& db_name, Env* env)
exit(1); exit(1);
} }
// Avoid relying on ReopenWritableFile which is not supported by all
// file systems. Create a new file and copy the old file contents to it.
std::string tmp_path = path_ + ".tmp";
st = fs->FileExists(tmp_path, opts, /*dbg*/ nullptr);
if (st.IsNotFound()) {
st = fs->RenameFile(path_, tmp_path, opts, /*dbg*/ nullptr);
// Either it should succeed or fail because src path doesn't exist
assert(st.ok() || st.IsPathNotFound());
} else {
// If path_ and tmp_path both exist, retain tmp_path as its
// guaranteed to be more complete. The order of operations are -
// 1. Rename path_ to tmp_path
// 2. Parse tmp_path contents
// 3. Create path_
// 4. Copy tmp_path contents to path_
// 5. Delete tmp_path
st = fs->DeleteFile(path_, opts, /*dbg*/ nullptr);
assert(st.ok() || st.IsPathNotFound());
}
uint64_t size = 0;
{ {
std::unique_ptr<FSSequentialFile> reader; std::unique_ptr<FSSequentialFile> reader;
Status s = Status s = fs->NewSequentialFile(tmp_path, FileOptions(), &reader,
fs->NewSequentialFile(path_, FileOptions(), &reader, /*dbg*/ nullptr); /*dbg*/ nullptr);
if (s.ok()) { if (s.ok()) {
// Load from file // Load from file
std::string id(24U, '\0'); std::string id(24U, '\0');
@ -60,37 +82,57 @@ UniqueIdVerifier::UniqueIdVerifier(const std::string& db_name, Env* env)
fprintf(stdout, "Warning: clearing corrupt unique id file\n"); fprintf(stdout, "Warning: clearing corrupt unique id file\n");
id_set_.clear(); id_set_.clear();
reader.reset(); reader.reset();
s = fs->DeleteFile(path_, opts, /*dbg*/ nullptr); s = fs->DeleteFile(tmp_path, opts, /*dbg*/ nullptr);
assert(s.ok()); assert(s.ok());
size = 0;
} }
break; break;
} }
size += 24U;
VerifyNoWrite(id); VerifyNoWrite(id);
} }
} else { } else {
// Newly created is ok. // Newly created is ok.
// But FileSystem doesn't tell us whether non-existence was the cause of // But FileSystem doesn't tell us whether non-existence was the cause of
// the failure. (Issue #9021) // the failure. (Issue #9021)
Status s2 = fs->FileExists(path_, opts, /*dbg*/ nullptr); Status s2 = fs->FileExists(tmp_path, opts, /*dbg*/ nullptr);
if (!s2.IsNotFound()) { if (!s2.IsNotFound()) {
fprintf(stderr, "Error opening unique id file: %s\n", fprintf(stderr, "Error opening unique id file: %s\n",
s.ToString().c_str()); s.ToString().c_str());
assert(false); assert(false);
} }
size = 0;
} }
} }
fprintf(stdout, "(Re-)verified %zu unique IDs\n", id_set_.size()); fprintf(stdout, "(Re-)verified %zu unique IDs\n", id_set_.size());
Status s = fs->ReopenWritableFile(path_, FileOptions(), &data_file_writer_,
/*dbg*/ nullptr); std::unique_ptr<FSWritableFile> file_writer;
if (!s.ok()) { st = fs->NewWritableFile(path_, FileOptions(), &file_writer, /*dbg*/ nullptr);
fprintf(stderr, "Error opening unique id file for append: %s\n", if (!st.ok()) {
s.ToString().c_str()); fprintf(stderr, "Error creating the unique ids file: %s\n",
st.ToString().c_str());
assert(false);
}
data_file_writer_.reset(
new WritableFileWriter(std::move(file_writer), path_, FileOptions()));
if (size > 0) {
st = CopyFile(fs.get(), tmp_path, data_file_writer_, size,
/*use_fsync*/ true, /*io_tracer*/ nullptr,
/*temparature*/ Temperature::kHot);
if (!st.ok()) {
fprintf(stderr, "Error copying contents of old unique id file: %s\n",
st.ToString().c_str());
assert(false); assert(false);
} }
} }
st = fs->DeleteFile(tmp_path, opts, /*dbg*/ nullptr);
assert(st.ok() || st.IsPathNotFound());
}
UniqueIdVerifier::~UniqueIdVerifier() { UniqueIdVerifier::~UniqueIdVerifier() {
data_file_writer_->Close(IOOptions(), /*dbg*/ nullptr); IOStatus s = data_file_writer_->Close();
assert(s.ok());
} }
void UniqueIdVerifier::VerifyNoWrite(const std::string& id) { void UniqueIdVerifier::VerifyNoWrite(const std::string& id) {
@ -112,14 +154,13 @@ void UniqueIdVerifier::Verify(const std::string& id) {
if (id_set_.size() >= 4294967) { if (id_set_.size() >= 4294967) {
return; return;
} }
IOStatus s = IOStatus s = data_file_writer_->Append(Slice(id));
data_file_writer_->Append(Slice(id), IOOptions(), /*dbg*/ nullptr);
if (!s.ok()) { if (!s.ok()) {
fprintf(stderr, "Error writing to unique id file: %s\n", fprintf(stderr, "Error writing to unique id file: %s\n",
s.ToString().c_str()); s.ToString().c_str());
assert(false); assert(false);
} }
s = data_file_writer_->Flush(IOOptions(), /*dbg*/ nullptr); s = data_file_writer_->Flush();
if (!s.ok()) { if (!s.ok()) {
fprintf(stderr, "Error flushing unique id file: %s\n", fprintf(stderr, "Error flushing unique id file: %s\n",
s.ToString().c_str()); s.ToString().c_str());

View File

@ -10,6 +10,7 @@
#include <unordered_set> #include <unordered_set>
#include "file/filename.h" #include "file/filename.h"
#include "file/writable_file_writer.h"
#include "rocksdb/db.h" #include "rocksdb/db.h"
#include "rocksdb/env.h" #include "rocksdb/env.h"
#include "rocksdb/file_system.h" #include "rocksdb/file_system.h"
@ -39,7 +40,7 @@ class UniqueIdVerifier {
std::mutex mutex_; std::mutex mutex_;
// IDs persisted to a hidden file inside DB dir // IDs persisted to a hidden file inside DB dir
std::string path_; std::string path_;
std::unique_ptr<FSWritableFile> data_file_writer_; std::unique_ptr<WritableFileWriter> data_file_writer_;
// Starting byte for which 8 bytes to check in memory within 24 byte ID // Starting byte for which 8 bytes to check in memory within 24 byte ID
size_t offset_; size_t offset_;
// Working copy of the set of 8 byte pieces // Working copy of the set of 8 byte pieces

View File

@ -18,13 +18,13 @@ namespace ROCKSDB_NAMESPACE {
// Utility function to copy a file up to a specified length // Utility function to copy a file up to a specified length
IOStatus CopyFile(FileSystem* fs, const std::string& source, IOStatus CopyFile(FileSystem* fs, const std::string& source,
const std::string& destination, uint64_t size, bool use_fsync, std::unique_ptr<WritableFileWriter>& dest_writer,
uint64_t size, bool use_fsync,
const std::shared_ptr<IOTracer>& io_tracer, const std::shared_ptr<IOTracer>& io_tracer,
const Temperature temperature) { const Temperature temperature) {
FileOptions soptions; FileOptions soptions;
IOStatus io_s; IOStatus io_s;
std::unique_ptr<SequentialFileReader> src_reader; std::unique_ptr<SequentialFileReader> src_reader;
std::unique_ptr<WritableFileWriter> dest_writer;
{ {
soptions.temperature = temperature; soptions.temperature = temperature;
@ -33,11 +33,6 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
if (!io_s.ok()) { if (!io_s.ok()) {
return io_s; return io_s;
} }
std::unique_ptr<FSWritableFile> destfile;
io_s = fs->NewWritableFile(destination, soptions, &destfile, nullptr);
if (!io_s.ok()) {
return io_s;
}
if (size == 0) { if (size == 0) {
// default argument means copy everything // default argument means copy everything
@ -48,8 +43,6 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
} }
src_reader.reset( src_reader.reset(
new SequentialFileReader(std::move(srcfile), source, io_tracer)); new SequentialFileReader(std::move(srcfile), source, io_tracer));
dest_writer.reset(
new WritableFileWriter(std::move(destfile), destination, soptions));
} }
char buffer[4096]; char buffer[4096];
@ -72,6 +65,30 @@ IOStatus CopyFile(FileSystem* fs, const std::string& source,
return dest_writer->Sync(use_fsync); return dest_writer->Sync(use_fsync);
} }
IOStatus CopyFile(FileSystem* fs, const std::string& source,
const std::string& destination, uint64_t size, bool use_fsync,
const std::shared_ptr<IOTracer>& io_tracer,
const Temperature temperature) {
FileOptions options;
IOStatus io_s;
std::unique_ptr<WritableFileWriter> dest_writer;
{
options.temperature = temperature;
std::unique_ptr<FSWritableFile> destfile;
io_s = fs->NewWritableFile(destination, options, &destfile, nullptr);
if (!io_s.ok()) {
return io_s;
}
dest_writer.reset(
new WritableFileWriter(std::move(destfile), destination, options));
}
return CopyFile(fs, source, dest_writer, size, use_fsync, io_tracer,
temperature);
}
// Utility function to create a file with the provided contents // Utility function to create a file with the provided contents
IOStatus CreateFile(FileSystem* fs, const std::string& destination, IOStatus CreateFile(FileSystem* fs, const std::string& destination,
const std::string& contents, bool use_fsync) { const std::string& contents, bool use_fsync) {

View File

@ -19,6 +19,11 @@
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
// use_fsync maps to options.use_fsync, which determines the way that // use_fsync maps to options.use_fsync, which determines the way that
// the file is synced after copying. // the file is synced after copying.
extern IOStatus CopyFile(FileSystem* fs, const std::string& source,
std::unique_ptr<WritableFileWriter>& dest_writer,
uint64_t size, bool use_fsync,
const std::shared_ptr<IOTracer>& io_tracer,
const Temperature temperature);
extern IOStatus CopyFile(FileSystem* fs, const std::string& source, extern IOStatus CopyFile(FileSystem* fs, const std::string& source,
const std::string& destination, uint64_t size, const std::string& destination, uint64_t size,
bool use_fsync, bool use_fsync,