diff options
author | Levi Tamasi <ltamasi@meta.com> | 2024-05-09 12:25:19 -0700 |
---|---|---|
committer | Facebook GitHub Bot <facebook-github-bot@users.noreply.github.com> | 2024-05-09 12:25:19 -0700 |
commit | 97e70906fab2bdc2878f5e05864165526dca79fe (patch) | |
tree | dc36c17d7cd0367c6df99bcc83c5f12484e56a16 /utilities | |
parent | 1a3357648fa2357097330e2abede82be1a749909 (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.cc | 31 | ||||
-rw-r--r-- | utilities/transactions/transaction_base.cc | 14 | ||||
-rw-r--r-- | utilities/transactions/transaction_test.cc | 31 | ||||
-rw-r--r-- | utilities/write_batch_with_index/write_batch_with_index.cc | 181 | ||||
-rw-r--r-- | utilities/write_batch_with_index/write_batch_with_index_test.cc | 122 |
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 |