summaryrefslogtreecommitdiff
path: root/utilities
diff options
context:
space:
mode:
authorLevi Tamasi <ltamasi@meta.com>2024-05-09 12:25:19 -0700
committerFacebook GitHub Bot <facebook-github-bot@users.noreply.github.com>2024-05-09 12:25:19 -0700
commit97e70906fab2bdc2878f5e05864165526dca79fe (patch)
treedc36c17d7cd0367c6df99bcc83c5f12484e56a16 /utilities
parent1a3357648fa2357097330e2abede82be1a749909 (diff)
Improve the sanity checks in (Multi)GetEntity and friends (#12630)
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/12630 The patch cleans up, improves, and brings into sync (to the extent possible without API signature changes) the sanity checks around the `GetEntity` / `MultiGetEntity` family of APIs, including the read-your-own-writes (`WriteBatchWithIndex`) and transaction layers. The checks are centralized in two main sets of entry points, namely in `DB(Impl)` and the "main" `GetEntityFromBatchAndDB` / `MultiGetEntityFromBatchAndDB` overloads in `WriteBatchWithIndex`. This eliminates the need to duplicate the checks in the transaction classes. Reviewed By: jaykorean Differential Revision: D57125741 fbshipit-source-id: 4dd059ef644a9b173fbba767538943397e4cc6cd
Diffstat (limited to 'utilities')
-rw-r--r--utilities/transactions/optimistic_transaction_test.cc31
-rw-r--r--utilities/transactions/transaction_base.cc14
-rw-r--r--utilities/transactions/transaction_test.cc31
-rw-r--r--utilities/write_batch_with_index/write_batch_with_index.cc181
-rw-r--r--utilities/write_batch_with_index/write_batch_with_index_test.cc122
5 files changed, 281 insertions, 98 deletions
diff --git a/utilities/transactions/optimistic_transaction_test.cc b/utilities/transactions/optimistic_transaction_test.cc
index eb522d73d..c75321323 100644
--- a/utilities/transactions/optimistic_transaction_test.cc
+++ b/utilities/transactions/optimistic_transaction_test.cc
@@ -1928,6 +1928,37 @@ TEST_P(OptimisticTransactionTest, PutEntityWriteConflictTxnTxn) {
}
}
+TEST_P(OptimisticTransactionTest, EntityReadSanityChecks) {
+ constexpr char foo[] = "foo";
+
+ std::unique_ptr<Transaction> txn(txn_db->BeginTransaction(WriteOptions()));
+ ASSERT_NE(txn, nullptr);
+
+ {
+ constexpr ColumnFamilyHandle* column_family = nullptr;
+ PinnableWideColumns columns;
+ ASSERT_TRUE(txn->GetEntity(ReadOptions(), column_family, foo, &columns)
+ .IsInvalidArgument());
+ }
+
+ {
+ constexpr PinnableWideColumns* columns = nullptr;
+ ASSERT_TRUE(txn->GetEntity(ReadOptions(), txn_db->DefaultColumnFamily(),
+ foo, columns)
+ .IsInvalidArgument());
+ }
+
+ {
+ ReadOptions read_options;
+ read_options.io_activity = Env::IOActivity::kGet;
+
+ PinnableWideColumns columns;
+ ASSERT_TRUE(txn->GetEntity(read_options, txn_db->DefaultColumnFamily(), foo,
+ &columns)
+ .IsInvalidArgument());
+ }
+}
+
INSTANTIATE_TEST_CASE_P(
InstanceOccGroup, OptimisticTransactionTest,
testing::Values(OccValidationPolicy::kValidateSerial,
diff --git a/utilities/transactions/transaction_base.cc b/utilities/transactions/transaction_base.cc
index 51031f636..046f848f6 100644
--- a/utilities/transactions/transaction_base.cc
+++ b/utilities/transactions/transaction_base.cc
@@ -289,22 +289,10 @@ Status TransactionBaseImpl::GetImpl(const ReadOptions& read_options,
pinnable_val);
}
-Status TransactionBaseImpl::GetEntity(const ReadOptions& _read_options,
+Status TransactionBaseImpl::GetEntity(const ReadOptions& read_options,
ColumnFamilyHandle* column_family,
const Slice& key,
PinnableWideColumns* columns) {
- if (_read_options.io_activity != Env::IOActivity::kUnknown &&
- _read_options.io_activity != Env::IOActivity::kGetEntity) {
- return Status::InvalidArgument(
- "Can only call GetEntity with `ReadOptions::io_activity` set to "
- "`Env::IOActivity::kUnknown` or `Env::IOActivity::kGetEntity`");
- }
-
- ReadOptions read_options(_read_options);
- if (read_options.io_activity == Env::IOActivity::kUnknown) {
- read_options.io_activity = Env::IOActivity::kGetEntity;
- }
-
return GetEntityImpl(read_options, column_family, key, columns);
}
diff --git a/utilities/transactions/transaction_test.cc b/utilities/transactions/transaction_test.cc
index 36d2715fb..7dbdb788e 100644
--- a/utilities/transactions/transaction_test.cc
+++ b/utilities/transactions/transaction_test.cc
@@ -7150,6 +7150,37 @@ TEST_P(TransactionTest, PutEntityWriteConflict) {
}
}
+TEST_P(TransactionTest, EntityReadSanityChecks) {
+ constexpr char foo[] = "foo";
+
+ std::unique_ptr<Transaction> txn(db->BeginTransaction(WriteOptions()));
+ ASSERT_NE(txn, nullptr);
+
+ {
+ constexpr ColumnFamilyHandle* column_family = nullptr;
+ PinnableWideColumns columns;
+ ASSERT_TRUE(txn->GetEntity(ReadOptions(), column_family, foo, &columns)
+ .IsInvalidArgument());
+ }
+
+ {
+ constexpr PinnableWideColumns* columns = nullptr;
+ ASSERT_TRUE(
+ txn->GetEntity(ReadOptions(), db->DefaultColumnFamily(), foo, columns)
+ .IsInvalidArgument());
+ }
+
+ {
+ ReadOptions read_options;
+ read_options.io_activity = Env::IOActivity::kGet;
+
+ PinnableWideColumns columns;
+ ASSERT_TRUE(
+ txn->GetEntity(read_options, db->DefaultColumnFamily(), foo, &columns)
+ .IsInvalidArgument());
+ }
+}
+
TEST_F(TransactionDBTest, CollapseKey) {
ASSERT_OK(ReOpen());
ASSERT_OK(db->Put({}, "hello", "world"));
diff --git a/utilities/write_batch_with_index/write_batch_with_index.cc b/utilities/write_batch_with_index/write_batch_with_index.cc
index d2e1816d4..834c2fa49 100644
--- a/utilities/write_batch_with_index/write_batch_with_index.cc
+++ b/utilities/write_batch_with_index/write_batch_with_index.cc
@@ -823,11 +823,41 @@ void WriteBatchWithIndex::MultiGetFromBatchAndDB(
}
Status WriteBatchWithIndex::GetEntityFromBatchAndDB(
- DB* db, const ReadOptions& read_options, ColumnFamilyHandle* column_family,
+ DB* db, const ReadOptions& _read_options, ColumnFamilyHandle* column_family,
const Slice& key, PinnableWideColumns* columns, ReadCallback* callback) {
- assert(db);
- assert(column_family);
- assert(columns);
+ if (!db) {
+ return Status::InvalidArgument(
+ "Cannot call GetEntityFromBatchAndDB without a DB object");
+ }
+
+ if (_read_options.io_activity != Env::IOActivity::kUnknown &&
+ _read_options.io_activity != Env::IOActivity::kGetEntity) {
+ return Status::InvalidArgument(
+ "Can only call GetEntityFromBatchAndDB with `ReadOptions::io_activity` "
+ "set to `Env::IOActivity::kUnknown` or `Env::IOActivity::kGetEntity`");
+ }
+
+ ReadOptions read_options(_read_options);
+ if (read_options.io_activity == Env::IOActivity::kUnknown) {
+ read_options.io_activity = Env::IOActivity::kGetEntity;
+ }
+
+ if (!column_family) {
+ return Status::InvalidArgument(
+ "Cannot call GetEntityFromBatchAndDB without a column family handle");
+ }
+
+ const Comparator* const ucmp = rep->comparator.GetComparator(column_family);
+ size_t ts_sz = ucmp ? ucmp->timestamp_size() : 0;
+ if (ts_sz > 0 && !read_options.timestamp) {
+ return Status::InvalidArgument("Must specify timestamp");
+ }
+
+ if (!columns) {
+ return Status::InvalidArgument(
+ "Cannot call GetEntityFromBatchAndDB without a PinnableWideColumns "
+ "object");
+ }
columns->Reset();
@@ -872,46 +902,78 @@ Status WriteBatchWithIndex::GetEntityFromBatchAndDB(
return s;
}
-Status WriteBatchWithIndex::GetEntityFromBatchAndDB(
- DB* db, const ReadOptions& read_options, ColumnFamilyHandle* column_family,
- const Slice& key, PinnableWideColumns* columns) {
+void WriteBatchWithIndex::MultiGetEntityFromBatchAndDB(
+ DB* db, const ReadOptions& _read_options, ColumnFamilyHandle* column_family,
+ size_t num_keys, const Slice* keys, PinnableWideColumns* results,
+ Status* statuses, bool sorted_input, ReadCallback* callback) {
+ assert(statuses);
+
if (!db) {
- return Status::InvalidArgument(
- "Cannot call GetEntityFromBatchAndDB without a DB object");
+ const Status s = Status::InvalidArgument(
+ "Cannot call MultiGetEntityFromBatchAndDB without a DB object");
+ for (size_t i = 0; i < num_keys; ++i) {
+ statuses[i] = s;
+ }
+ return;
+ }
+
+ if (_read_options.io_activity != Env::IOActivity::kUnknown &&
+ _read_options.io_activity != Env::IOActivity::kMultiGetEntity) {
+ const Status s = Status::InvalidArgument(
+ "Can only call MultiGetEntityFromBatchAndDB with "
+ "`ReadOptions::io_activity` set to `Env::IOActivity::kUnknown` or "
+ "`Env::IOActivity::kMultiGetEntity`");
+ for (size_t i = 0; i < num_keys; ++i) {
+ if (statuses[i].ok()) {
+ statuses[i] = s;
+ }
+ }
+ return;
+ }
+
+ ReadOptions read_options(_read_options);
+ if (read_options.io_activity == Env::IOActivity::kUnknown) {
+ read_options.io_activity = Env::IOActivity::kMultiGetEntity;
}
if (!column_family) {
- return Status::InvalidArgument(
- "Cannot call GetEntityFromBatchAndDB without a column family handle");
+ const Status s = Status::InvalidArgument(
+ "Cannot call MultiGetEntityFromBatchAndDB without a column family "
+ "handle");
+ for (size_t i = 0; i < num_keys; ++i) {
+ statuses[i] = s;
+ }
+ return;
}
const Comparator* const ucmp = rep->comparator.GetComparator(column_family);
- size_t ts_sz = ucmp ? ucmp->timestamp_size() : 0;
+ const size_t ts_sz = ucmp ? ucmp->timestamp_size() : 0;
if (ts_sz > 0 && !read_options.timestamp) {
- return Status::InvalidArgument("Must specify timestamp");
+ const Status s = Status::InvalidArgument("Must specify timestamp");
+ for (size_t i = 0; i < num_keys; ++i) {
+ statuses[i] = s;
+ }
+ return;
}
- if (!columns) {
- return Status::InvalidArgument(
- "Cannot call GetEntityFromBatchAndDB without a PinnableWideColumns "
- "object");
+ if (!keys) {
+ const Status s = Status::InvalidArgument(
+ "Cannot call MultiGetEntityFromBatchAndDB without keys");
+ for (size_t i = 0; i < num_keys; ++i) {
+ statuses[i] = s;
+ }
+ return;
}
- constexpr ReadCallback* callback = nullptr;
-
- return GetEntityFromBatchAndDB(db, read_options, column_family, key, columns,
- callback);
-}
-
-void WriteBatchWithIndex::MultiGetEntityFromBatchAndDB(
- DB* db, const ReadOptions& read_options, ColumnFamilyHandle* column_family,
- size_t num_keys, const Slice* keys, PinnableWideColumns* results,
- Status* statuses, bool sorted_input, ReadCallback* callback) {
- assert(db);
- assert(column_family);
- assert(keys);
- assert(results);
- assert(statuses);
+ if (!results) {
+ const Status s = Status::InvalidArgument(
+ "Cannot call MultiGetEntityFromBatchAndDB without "
+ "PinnableWideColumns objects");
+ for (size_t i = 0; i < num_keys; ++i) {
+ statuses[i] = s;
+ }
+ return;
+ }
struct MergeTuple {
MergeTuple(const Slice& _key, Status* _s, MergeContext&& _merge_context,
@@ -990,8 +1052,8 @@ void WriteBatchWithIndex::MultiGetEntityFromBatchAndDB(
static_cast_with_check<DBImpl>(db->GetRootDB())
->PrepareMultiGetKeys(sorted_keys.size(), sorted_input, &sorted_keys);
static_cast_with_check<DBImpl>(db->GetRootDB())
- ->MultiGetWithCallback(read_options, column_family, callback,
- &sorted_keys);
+ ->MultiGetEntityWithCallback(read_options, column_family, callback,
+ &sorted_keys);
for (const auto& merge : merges) {
if (merge.s->ok() || merge.s->IsNotFound()) { // DB lookup succeeded
@@ -1001,57 +1063,6 @@ void WriteBatchWithIndex::MultiGetEntityFromBatchAndDB(
}
}
-void WriteBatchWithIndex::MultiGetEntityFromBatchAndDB(
- DB* db, const ReadOptions& read_options, ColumnFamilyHandle* column_family,
- size_t num_keys, const Slice* keys, PinnableWideColumns* results,
- Status* statuses, bool sorted_input) {
- assert(statuses);
-
- if (!db) {
- for (size_t i = 0; i < num_keys; ++i) {
- statuses[i] = Status::InvalidArgument(
- "Cannot call MultiGetEntityFromBatchAndDB without a DB object");
- }
- }
-
- if (!column_family) {
- for (size_t i = 0; i < num_keys; ++i) {
- statuses[i] = Status::InvalidArgument(
- "Cannot call MultiGetEntityFromBatchAndDB without a column family "
- "handle");
- }
- }
-
- const Comparator* const ucmp = rep->comparator.GetComparator(column_family);
- const size_t ts_sz = ucmp ? ucmp->timestamp_size() : 0;
- if (ts_sz > 0 && !read_options.timestamp) {
- for (size_t i = 0; i < num_keys; ++i) {
- statuses[i] = Status::InvalidArgument("Must specify timestamp");
- }
- return;
- }
-
- if (!keys) {
- for (size_t i = 0; i < num_keys; ++i) {
- statuses[i] = Status::InvalidArgument(
- "Cannot call MultiGetEntityFromBatchAndDB without keys");
- }
- }
-
- if (!results) {
- for (size_t i = 0; i < num_keys; ++i) {
- statuses[i] = Status::InvalidArgument(
- "Cannot call MultiGetEntityFromBatchAndDB without "
- "PinnableWideColumns objects");
- }
- }
-
- constexpr ReadCallback* callback = nullptr;
-
- MultiGetEntityFromBatchAndDB(db, read_options, column_family, num_keys, keys,
- results, statuses, sorted_input, callback);
-}
-
void WriteBatchWithIndex::SetSavePoint() { rep->write_batch.SetSavePoint(); }
Status WriteBatchWithIndex::RollbackToSavePoint() {
diff --git a/utilities/write_batch_with_index/write_batch_with_index_test.cc b/utilities/write_batch_with_index/write_batch_with_index_test.cc
index bf7230331..d706682a5 100644
--- a/utilities/write_batch_with_index/write_batch_with_index_test.cc
+++ b/utilities/write_batch_with_index/write_batch_with_index_test.cc
@@ -2973,6 +2973,128 @@ TEST_P(WriteBatchWithIndexTest, GetEntityFromBatch) {
}
}
+TEST_P(WriteBatchWithIndexTest, EntityReadSanityChecks) {
+ ASSERT_OK(OpenDB());
+
+ constexpr char foo[] = "foo";
+ constexpr char bar[] = "bar";
+ constexpr size_t num_keys = 2;
+
+ {
+ constexpr DB* db = nullptr;
+ PinnableWideColumns columns;
+ ASSERT_TRUE(batch_
+ ->GetEntityFromBatchAndDB(db, ReadOptions(),
+ db_->DefaultColumnFamily(), foo,
+ &columns)
+ .IsInvalidArgument());
+ }
+
+ {
+ constexpr ColumnFamilyHandle* column_family = nullptr;
+ PinnableWideColumns columns;
+ ASSERT_TRUE(batch_
+ ->GetEntityFromBatchAndDB(db_, ReadOptions(), column_family,
+ foo, &columns)
+ .IsInvalidArgument());
+ }
+
+ {
+ constexpr PinnableWideColumns* columns = nullptr;
+ ASSERT_TRUE(batch_
+ ->GetEntityFromBatchAndDB(db_, ReadOptions(),
+ db_->DefaultColumnFamily(), foo,
+ columns)
+ .IsInvalidArgument());
+ }
+
+ {
+ ReadOptions read_options;
+ read_options.io_activity = Env::IOActivity::kGet;
+
+ PinnableWideColumns columns;
+ ASSERT_TRUE(batch_
+ ->GetEntityFromBatchAndDB(db_, read_options,
+ db_->DefaultColumnFamily(), foo,
+ &columns)
+ .IsInvalidArgument());
+ }
+
+ {
+ constexpr DB* db = nullptr;
+ std::array<Slice, num_keys> keys{{foo, bar}};
+ std::array<PinnableWideColumns, num_keys> results;
+ std::array<Status, num_keys> statuses;
+ constexpr bool sorted_input = false;
+
+ batch_->MultiGetEntityFromBatchAndDB(
+ db, ReadOptions(), db_->DefaultColumnFamily(), num_keys, keys.data(),
+ results.data(), statuses.data(), sorted_input);
+
+ ASSERT_TRUE(statuses[0].IsInvalidArgument());
+ ASSERT_TRUE(statuses[1].IsInvalidArgument());
+ }
+
+ {
+ constexpr ColumnFamilyHandle* column_family = nullptr;
+ std::array<Slice, num_keys> keys{{foo, bar}};
+ std::array<PinnableWideColumns, num_keys> results;
+ std::array<Status, num_keys> statuses;
+ constexpr bool sorted_input = false;
+
+ batch_->MultiGetEntityFromBatchAndDB(db_, ReadOptions(), column_family,
+ num_keys, keys.data(), results.data(),
+ statuses.data(), sorted_input);
+
+ ASSERT_TRUE(statuses[0].IsInvalidArgument());
+ ASSERT_TRUE(statuses[1].IsInvalidArgument());
+ }
+
+ {
+ constexpr Slice* keys = nullptr;
+ std::array<PinnableWideColumns, num_keys> results;
+ std::array<Status, num_keys> statuses;
+ constexpr bool sorted_input = false;
+
+ batch_->MultiGetEntityFromBatchAndDB(
+ db_, ReadOptions(), db_->DefaultColumnFamily(), num_keys, keys,
+ results.data(), statuses.data(), sorted_input);
+
+ ASSERT_TRUE(statuses[0].IsInvalidArgument());
+ ASSERT_TRUE(statuses[1].IsInvalidArgument());
+ }
+
+ {
+ std::array<Slice, num_keys> keys{{foo, bar}};
+ constexpr PinnableWideColumns* results = nullptr;
+ std::array<Status, num_keys> statuses;
+ constexpr bool sorted_input = false;
+
+ batch_->MultiGetEntityFromBatchAndDB(
+ db_, ReadOptions(), db_->DefaultColumnFamily(), num_keys, keys.data(),
+ results, statuses.data(), sorted_input);
+
+ ASSERT_TRUE(statuses[0].IsInvalidArgument());
+ ASSERT_TRUE(statuses[1].IsInvalidArgument());
+ }
+
+ {
+ ReadOptions read_options;
+ read_options.io_activity = Env::IOActivity::kMultiGet;
+
+ std::array<Slice, num_keys> keys{{foo, bar}};
+ std::array<PinnableWideColumns, num_keys> results;
+ std::array<Status, num_keys> statuses;
+ constexpr bool sorted_input = false;
+
+ batch_->MultiGetEntityFromBatchAndDB(
+ db_, read_options, db_->DefaultColumnFamily(), num_keys, keys.data(),
+ results.data(), statuses.data(), sorted_input);
+ ASSERT_TRUE(statuses[0].IsInvalidArgument());
+ ASSERT_TRUE(statuses[1].IsInvalidArgument());
+ }
+}
+
INSTANTIATE_TEST_CASE_P(WBWI, WriteBatchWithIndexTest, testing::Bool());
} // namespace ROCKSDB_NAMESPACE