[BUG] - Mock reliable collections do not serialize, or at least, deep copy inserted data
See original GitHub issueDescribe the bug
In the reliable collections docs, the authors describe scenarios in which programmers might accidentally cause bugs by updating objects in-memory, but not in the reliable collections. This is an important behavioral difference that reliable collections have over regular collections - updating a reference to data that was inserted into the reliable collection does not update the stored data.
However, in our unit tests, we have noticed that the mock reliable collections do not model this behavior. Updating a reference to data that is stored in a mock reliable collection will apply to the stored data. This makes our unit tests behave differently than in integration tests, and has been the source of some confusion. Thus, in unit tests we have resorted to manually making deep copies of the data before storing them, by doing a serialization round trip.
Reliable collections by default support data contract serialization. It might be some work to support the default serialization, and not everyone is guaranteed to use it. However, custom serialization is also supported through the IReliableStateManager.TryAddStateSerializer() method. I noticed that the MockReliableStateManager.TryAddStateSerializer() method is currently stubbed out. Is it feasible to implement this method to search for a serializer for a particular type before inserting data, so a deep copy is guaranteed for that type? Since we already use custom serialization, this would be enough to fix this for us.
I decided to open an issue first, to gauge interest in supporting this behavior before attempting a contribution.
To Reproduce
Here is a program that demonstrates this, modeled after the example in the reliable collections docs.
namespace MockReliableCollectionsBug
{
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using ServiceFabric.Mocks;
using System;
using System.Threading.Tasks;
public static class MockReliableCollectionsBug
{
public class User
{
public string Name { get; set; }
public DateTime LastLogin { get; set; }
}
public static async Task Main(string[] args)
{
MockReliableStateManager stateManager = new MockReliableStateManager();
IReliableDictionary2<string, User> userDict = await stateManager.GetOrAddAsync<IReliableDictionary2<string, User>>("test");
User user = new User { Name = "Gail", LastLogin = DateTime.UtcNow };
using (ITransaction tx = stateManager.CreateTransaction())
{
await userDict.AddAsync(tx, user.Name, user);
await tx.CommitAsync();
}
// Update the in-memory user's LastLogin.
user.LastLogin = DateTime.UtcNow;
using (ITransaction tx = stateManager.CreateTransaction())
{
ConditionalValue<User> storedUser = await userDict.TryGetValueAsync(tx, user.Name);
// This should output false, as the storedUser's LastLogin should be the original value, but
// it returns true, since the stored value is a direct reference, not a copy of the in-memory value
// which has since been updated.
Console.WriteLine(user.LastLogin == storedUser.Value.LastLogin);
await tx.CommitAsync();
}
}
}
}
Expected behavior
Console should output false
.
Issue Analytics
- State:
- Created 2 years ago
- Comments:14 (7 by maintainers)
Top GitHub Comments
I was going for an approach that would closely mimic the real deal (which uses file streams). The end result is the same I suppose. Good point about the serializer type check.
I’ve also copied your key /value serializer approach in my latest push.
edit: I’ve also taken your serializer collection approach.
Sounds like a plan. Let’s make sure we follow the docs, concerning types (value types vs reference types). I’ve also got the plan to deprecate .NET 4.5 support and move everything to .NET6. You can find the branch here.