Write failure can be permanently fatal and break WriteBatch atomicity (#13428)

Summary:
This adds a test that attempts DeleteRange() with PlainTable (not supported) and shows that it not only puts the DB in failed write mode, it (a) breaks WriteBatch atomicity for readers, because they can see just part of a failed WriteBatch, and (b) makes the DB not recoverable (without manual intervention) if using WAL.

Note: WriteBatch atomicity is not clearly documented but indicated at the top of write_batch.h and the wiki page for Transactions, even without Transactions.

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

Test Plan: this is the test

Reviewed By: anand1976

Differential Revision: D70332226

Pulled By: pdillinger

fbshipit-source-id: 67bc4de68833a80578e48baa9d3a4f23f1600f3c
This commit is contained in:
Peter Dillinger 2025-02-27 11:37:56 -08:00 committed by Facebook GitHub Bot
parent d1f383b8eb
commit ebaeb03648

View File

@ -1338,6 +1338,50 @@ TEST_P(PlainTableDBTest, AdaptiveTable) {
INSTANTIATE_TEST_CASE_P(PlainTableDBTest, PlainTableDBTest, ::testing::Bool());
TEST_P(PlainTableDBTest, DeleteRangeNotSupported) {
// XXX: After attempting DeleteRange with PlainTable, Writes will permanently
// fail. Even if re-opening the DB, if WAL is used, the WAL is not recoverable
// (without manual intervention). Furthermore, a partial write batch can
// be exposed to readers, breaking WriteBatch atomicity.
for (bool use_write_batch : {/*false, */ true}) {
DestroyAndReopen();
ASSERT_OK(Put("a0001111", "1"));
ASSERT_OK(Put("b0001111", "2"));
ASSERT_OK(Put("c0001111", "3"));
if (use_write_batch) {
WriteBatch wb;
ASSERT_OK(wb.Put("d0001111", "4"));
ASSERT_OK(wb.DeleteRange("a", "b"));
ASSERT_OK(wb.Put("e0001111", "5"));
ASSERT_EQ(dbfull()->Write({}, &wb).code(), Status::Code::kNotSupported);
} else {
ASSERT_EQ(dbfull()->DeleteRange({}, "az", "bz").code(),
Status::Code::kNotSupported);
}
ASSERT_EQ(Get("a0001111"), "1");
ASSERT_EQ(Get("b0001111"), "2");
ASSERT_EQ(Get("c0001111"), "3");
if (use_write_batch) {
// XXX: broken WriteBatch atomicity
ASSERT_EQ(Get("d0001111"), "4");
} else {
ASSERT_EQ(Get("d0001111"), "NOT_FOUND");
}
ASSERT_EQ(Get("e0001111"), "NOT_FOUND");
ASSERT_EQ(Put("e0001111", "5").code(), Status::Code::kNotSupported);
ASSERT_EQ(Get("e0001111"), "NOT_FOUND");
// Even trying to flush
ASSERT_EQ(dbfull()->TEST_FlushMemTable().code(),
Status::Code::kNotSupported);
// XXX: WAL is not recoverable
ASSERT_EQ(TryReopen().code(), Status::Code::kNotSupported);
}
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {