-
Notifications
You must be signed in to change notification settings - Fork 6.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
db_stress: db_stress segmentation fault #9175
base: main
Are you sure you want to change the base?
Conversation
db_stress seg faults with below commands. "rm -rf /tmp/rocksdbtest*; sudo ./db_stress --ops_per_thread=1000 --reopen=5 --fs_uri=zenfs://dev:nvmeXnY" "rm -rf /tmp/rocksdbtest*; db_stress --ops_per_thread=1000 --reopen=5" ======================================= Error opening unique id file for append: IO error: No such file or directory: While open a file for appending: /tmp/rocksdbtest-0/dbstress/.unique_ids: No such file or directory Choosing random keys with no overwrite Creating 2621440 locks Starting continuous_verification_thread 2021/11/15-08:46:49 Initializing worker threads 2021/11/15-08:46:49 Starting database operations 2021/11/15-08:46:49 Reopening database for the 1th time WARNING: prefix_size is non-zero but memtablerep != prefix_hash DB path: [/tmp/rocksdbtest-0/dbstress] Segmentation fault ======================================= db_stress is also broken for custom filesystems such as ZenFS(RocksDb plugin). This happens on a new database, UniqueIdVerifier's constructor tries to read the ".unique_ids" file, if the file is not present, the data_file_writer_ is set as an invalid(null) pointer and in subsequent calls (~UniqueIdVerifier() and UniqueIdVerifier::Verify()) it accesses this null pointer and crashes. This patch uses the filesystem interface from relevant environment and creates the .unique_ids file if it does not exist, so that data_file_writer_ points to a valid file and is not null. This patch also fixes the issue for custom filesystems. Signed-off-by: Aravind Ramesh <Aravind.Ramesh@wdc.com>
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR!
I initially tried passing in Env or FileSystem, but db_stress_env might use FaultInjectionTestFS and we don't want fault injection in this test-supporting I/O path. (It's not an I/O path under test.) @zhichao-cao or @siying know the right answer here?
And apparently you're running db_stress in release build. I didn't know that was done, because the point of db_stress is to detect bugs and the debug build is generally much more sensitive to anomalies. We can change my assert(false) cases to std::abort() to ensure the intended invariants even in release build.
@@ -84,7 +88,8 @@ UniqueIdVerifier::UniqueIdVerifier(const std::string& db_name) | |||
} | |||
|
|||
UniqueIdVerifier::~UniqueIdVerifier() { | |||
data_file_writer_->Close(IOOptions(), /*dbg*/ nullptr); | |||
if (data_file_writer_) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
data_file_writer_ is not intended to be nullable, because these checks are not intended to be optional. (If they were optional, Verify() would also need updating)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, it is expected to be non-null, so will remove this check.
Status s2 = fs->FileExists(path_, opts, /*dbg*/ nullptr); | ||
if (s2.IsNotFound()) { | ||
std::unique_ptr<FSWritableFile> wf; | ||
s = fs->NewWritableFile(path_, FileOptions(), &wf, /*dbg*/ nullptr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unnecessary according to the API docs of ReopenWritableFile
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that you pointed it out, I dug in a bit more into it and turns out the StressTest() constructor is deleting the "dbstress" directory under "/tmp/rocksdbtest-0/" directory because "--destroy_db_initially" option is by default on.
In UniqueIdVerifier() constructor, ReOpenWritableFile() tries to create the file
"/tmp/rocksdbtest-0/dbstress/.unique_ids" but it fails because the directory dbstress is missing, so it fails to create the "unique_ids"file and "data_file_writer_" becomes an invalid pointer, causing seg faults in ~UniqueIdVerifier() and Verify() in release builds and asserting on assert(false) in debug builds.
So, below command seg faults on both debug and release builds
"rm -rf /tmp/rocksdbtest*; db_stress --ops_per_thread=1000 --reopen=5"
Below command passes on both debug and release builds
"rm -rf /tmp/rocksdbtest*; db_stress --ops_per_thread=1000 --reopen=5 --destroy_db_initially=0"
So I added a change to create the db directory if it is missing, so that ReOpenWritableFile() can work as expected and there is no need of NewWritableFile().
// Use default FileSystem to avoid fault injection, etc. | ||
FileSystem& fs = *FileSystem::Default(); | ||
// Use the FileSystem from the environment | ||
const std::shared_ptr<rocksdb::FileSystem>fs = env->GetFileSystem(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No rocksdb::
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
@@ -27,14 +27,14 @@ UniqueIdVerifier::UniqueIdVerifier(const std::string& db_name) | |||
// very good probability for the quantities in this test. | |||
offset_ = Random::GetTLSInstance()->Uniform(17); // 0 to 16 | |||
|
|||
// Use default FileSystem to avoid fault injection, etc. | |||
FileSystem& fs = *FileSystem::Default(); | |||
// Use the FileSystem from the environment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As described in the comment, for the listener, we use the default FS (a new instance) such that, the calls will not executed by fault_injection_fs. If we use the one from env_, then we may get IOError here, which is not expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A better way might be, we create another wrapper for the customized FileSystem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion.
Could you please elaborate a bit more on the new wrapper that you are suggesting ? What would this wrapper include/exclude ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zhichao-cao @aravind-wdc What is the goal here? You want to get the FileSystem from inside the FaultInjectionTestFS? Could you just use static_cast<FileSystem*>(env->GetFileSystem()->Inner())
Thanks @pdillinger and @zhichao-cao for the review comments. PR 1, to fix the seg fault issue for normal db_stress. Below is the patch for that, I will send it as a new PR. Basically creates the db directory if missing.
PR 2: Once I get the clarification from @zhichao-cao on the wrapper, I will send another PR which would use the environment based filesystem so that custom filesystems like ZenFS can run db_stress. Thoughts ? |
I'm not convinced this is needed if using the right Env/FS.
I think we need to save a copy of raw_env for this kind of use before we wrap it here: https://github.com/facebook/rocksdb/blob/main/db_stress_tool/db_stress_tool.cc#L101 |
I do not think you do not need to save the raw env as you can get its file system using target(). If you really need to, you could verify whether the outer FileSystem was a FaultInjection one by IsInstanceOf. One point of the configurable/customizable infrastructure was to eliminate some of the guess work here and make things simpler... |
That approach is less robust to code evolution, e.g. if another layer of indirection is added, mysterious failures might ensue. |
Since the issue is that the "dbstress" directory is not available at this point, even with the right Env/FS, it will fail to create the ".unique_ids" file. Creating the "dbstress" directory if it is missing, ensures that ReopenWritableFile() is able to create the file. |
OK, yes, thanks. For whatever reason, our db_stress setup creates the directory before starting db_stress. |
Thanks. I have sent a PR #9219 to fix it in db_stress for regular use case. |
Thanks for pulling the earlier change. So, I have submitted another PR(#9352) with changes where faultinjection file system is not passed to dbstresslistener, even when fault injection is enabled. Please review. |
@pdillinger Could you please take a look at PR #9352 , which is based on the feedback I got here. |
db_stress seg faults with below commands.
db_stress is also broken for custom filesystems such as ZenFS(RocksDb plugin).
This happens on a new database, UniqueIdVerifier's constructor tries to read the
".unique_ids" file, if the file is not present, the data_file_writer_ is set as
an invalid(null) pointer and in subsequent calls (~UniqueIdVerifier()
and UniqueIdVerifier::Verify()) it accesses this null pointer and crashes.
This patch uses the filesystem interface from relevant environment and creates
the .unique_ids file if it does not exist, so that data_file_writer_
points to a valid file and is not null. This patch also fixes the
issue for custom filesystems.
Signed-off-by: Aravind Ramesh Aravind.Ramesh@wdc.com