I have this test that fails when using API version 300 or greater. The default API version selected was 200 up until recently, so this is probably an old change that went unnoticed by me?
The test checks that a Snapshot read does not see the writes made by the transaction as long as it is not committed. I remember that things where changed in the past regarding this, but I thought that I already handled it. Maybe I missed something?
[Test]
public async Task Test_Read_Isolation_From_Writes()
{
// By default:
// - Regular reads see the writes made by the transaction itself, but not the writes made by other transactions that committed in between
// - Snapshot reads never see the writes made since the transaction read version, including the writes made by the transaction itself
//Fdb.Start(200); // <-- the test passes
//Fdb.Start(300); // <-- the test fails
using (var db = await OpenTestPartitionAsync())
{
var location = db.Partition.ByKey("test");
await db.ClearRangeAsync(location, this.Cancellation);
var A = location.Keys.Encode("A");
var B = location.Keys.Encode("B");
var C = location.Keys.Encode("C");
var D = location.Keys.Encode("D");
// Reads (before and after):
// - A and B will use regular reads
// - C and D will use snapshot reads
// Writes:
// - A and C will be modified by the transaction itself
// - B and D will be modified by a different transaction
await db.WriteAsync((tr) =>
{
tr.Set(A, Slice.FromString("a"));
tr.Set(B, Slice.FromString("b"));
tr.Set(C, Slice.FromString("c"));
tr.Set(D, Slice.FromString("d"));
}, this.Cancellation);
Log("Initial db state:");
await DumpSubspace(db, location);
using (var tr = db.BeginTransaction(this.Cancellation))
{
// check initial state
Assert.That((await tr.GetAsync(A)).ToStringUtf8(), Is.EqualTo("a"));
Assert.That((await tr.GetAsync(B)).ToStringUtf8(), Is.EqualTo("b"));
Assert.That((await tr.Snapshot.GetAsync(C)).ToStringUtf8(), Is.EqualTo("c"));
Assert.That((await tr.Snapshot.GetAsync(D)).ToStringUtf8(), Is.EqualTo("d"));
// mutate (not yet comitted)
tr.Set(A, Slice.FromString("aa"));
tr.Set(C, Slice.FromString("cc"));
await db.WriteAsync((tr2) =>
{ // have another transaction change B and D under our nose
tr2.Set(B, Slice.FromString("bb"));
tr2.Set(D, Slice.FromString("dd"));
}, this.Cancellation);
// check what the transaction sees
Assert.That((await tr.GetAsync(A)).ToStringUtf8(), Is.EqualTo("aa"), "The transaction own writes should change the value of regular reads");
Assert.That((await tr.GetAsync(B)).ToStringUtf8(), Is.EqualTo("b"), "Other transaction writes should not change the value of regular reads");
//FAIL: test fails here because we read "CC" ??
Assert.That((await tr.Snapshot.GetAsync(C)).ToStringUtf8(), Is.EqualTo("c"), "The transaction own writes should not change the value of snapshot reads");
Assert.That((await tr.Snapshot.GetAsync(D)).ToStringUtf8(), Is.EqualTo("d"), "Other transaction writes should not change the value of snapshot reads");
//note: committing here would conflict
}
}
}
- With API version 200: PASS
- With API version 300 or more: FAIL
=== FoundationDB.Client.Tests.TransactionFacts.Test_Read_Isolation_From_Writes() === 11:32:53.9300588
Initial db state:
Dumping content of subspace Subspace(<15>.<02>test<00>) :
- ("A",) = a
- ("B",) = b
- ("C",) = c
- ("D",) = d
> Found 4 values
Test 'FoundationDB.Client.Tests.TransactionFacts.Test_Read_Isolation_From_Writes' failed:
The transaction own writes should not change the value of snapshot reads
Expected string length 1 but was 2. Strings differ at index 1.
Expected: "c"
But was: "cc"
------------^
TransactionFacts.cs(1309,0): at FoundationDB.Client.Tests.TransactionFacts.<Test_Read_Isolation_From_Writes>d__27.MoveNext()
at NUnit.Framework.AsyncInvocationRegion.AsyncTaskInvocationRegion.WaitForPendingOperationsToComplete(Object invocationResult)
at NUnit.Core.NUnitAsyncTestMethod.RunTestMethod()