### Bug Fixes
* Since 6.15.0, `TransactionDB` returns error `Status`es from calls to `DeleteRange()` and calls to `Write()` where the `WriteBatch` contains a range deletion. Previously such operations may have succeeded while not providing the expected transactional guarantees. There are certain cases where range deletion can still be used on such DBs; see the API doc on `TransactionDB::DeleteRange()` for details.
* `OptimisticTransactionDB` now returns error `Status`es from calls to `DeleteRange()` and calls to `Write()` where the `WriteBatch` contains a range deletion. Previously such operations may have succeeded while not providing the expected transactional guarantees.
+* Fix `WRITE_PREPARED`, `WRITE_UNPREPARED` TransactionDB `MultiGet()` may return uncommitted data with snapshot.
## 6.17.1 (01/28/2021)
### Behavior Changes
}
// If timestamp is used, we use read callback to ensure <key,t,s> is returned
// only if t <= read_opts.timestamp and s <= snapshot.
- if (ts_sz > 0 && !get_impl_options.callback) {
+ if (ts_sz > 0) {
+ assert(!get_impl_options
+ .callback); // timestamp with callback is not supported
read_cb.Refresh(snapshot);
get_impl_options.callback = &read_cb;
}
}
GetWithTimestampReadCallback timestamp_read_callback(0);
- ReadCallback* read_callback = nullptr;
+ ReadCallback* read_callback = callback;
if (read_options.timestamp && read_options.timestamp->size() > 0) {
+ assert(!read_callback); // timestamp with callback is not supported
timestamp_read_callback.Refresh(consistent_seqnum);
read_callback = ×tamp_read_callback;
}
}
}
+TEST_P(TransactionTest, MultiGetSnapshot) {
+ WriteOptions write_options;
+ TransactionOptions transaction_options;
+ Transaction* txn1 = db->BeginTransaction(write_options, transaction_options);
+
+ Slice key = "foo";
+
+ Status s = txn1->Put(key, "bar");
+ ASSERT_OK(s);
+
+ s = txn1->SetName("test");
+ ASSERT_OK(s);
+
+ s = txn1->Prepare();
+ ASSERT_OK(s);
+
+ // Get snapshot between prepare and commit
+ // Un-committed data should be invisible to other transactions
+ const Snapshot* s1 = db->GetSnapshot();
+
+ s = txn1->Commit();
+ ASSERT_OK(s);
+ delete txn1;
+
+ Transaction* txn2 = db->BeginTransaction(write_options, transaction_options);
+ ReadOptions read_options;
+ read_options.snapshot = s1;
+
+ std::vector<Slice> keys;
+ std::vector<PinnableSlice> values(1);
+ std::vector<Status> statuses(1);
+ keys.push_back(key);
+ auto cfd = db->DefaultColumnFamily();
+ txn2->MultiGet(read_options, cfd, 1, keys.data(), values.data(),
+ statuses.data());
+ ASSERT_TRUE(statuses[0].IsNotFound());
+ delete txn2;
+
+ db->ReleaseSnapshot(s1);
+}
+
TEST_P(TransactionTest, ColumnFamiliesTest2) {
WriteOptions write_options;
ReadOptions read_options, snapshot_read_options;