For C# developers: SAFE.DataStructures

As I mentioned both in SAFE CrossRoads, and in the topic ServiceFabric and
SAFENetwork
, I have been planning to implement some other datastructures for regular as well as not so regular use in C#.

First out is a persistent dictionary. The API is like any C# dictionary type very simple, in comparison to the direct interaction with the key-value storage of SAFENetwork:

public interface IPersistentDictionary<TKey, TVal>
    {
        Task<long> GetCountAsync();
        Task<bool> ContainsKeyAsync(TKey key);
        Task<long> GetEntryVersionAsync(TKey key);
        Task<Result<bool>> SetAsync(TKey key, TVal val, long expectedVersion);
        Task ClearAsync();
        Task TryRemoveAsync(TKey key);
        Task<TVal> GetOrAddAsync(TKey key, TVal value);
        Task<ConditionalValue<TVal>> TryGetValueAsync(TKey key);
        Task<ConcurrentDictionary<TKey, TVal>> GetDictAsync();
    }

All of the above, except ClearAsync and TryRemoveAsync, is implemented and covered by unit tests against Mock network: GitHub repo https://github.com/oetyng/SAFE.DataStructures
Much of the code is very similar to the latest EventStore code (with sharding and so on, to increase capacity), and I think I will actually be able to include the eventstore as a datastructure in this library (we’ll see).

Now, those of you who read my post about ServiceFabric and
SAFENetwork
, will notice that this is something a little bit different.
The plan to implement the actual IReliable[Dictionary/Queue] interfaces of ServiceFabric has changed a bit, as anyone who wants to use them in this microservices orchestration framework, can implement an adaptor. That’s a valid option I have done in production anyway, and removes dependency on ServiceFabric.

However, I still aim to implement transactions, just as they work in ServiceFabric. For those who don’t know, that would mean that we do something like this:

using (var tx = _stateManager.CreateTransaction())
{
    await _dict.SetAsync(tx, key1, value1);
    await _dict.SetAsync(tx, key2, value2);
    await _queue.EnqueueAsync(tx, item);
    await tx.CommitAsync();
}

and they would all succeed or fail together. And that is something really useful.
(Another thing that thrills me about all this here, the data would be (almost) immediately backed up, replicated and available from anywhere in the world. It just excites me everytime I think of it :slight_smile: )

Now, implementing this transaction over SAFENetwork, is not going to be trivial. But I have an idea. Probably going to be quite slow, but that’s were I’ll start. I’ll soon get some code up on that. If anyone has any suggestions on how we can improve on that, please share them!
As some of you might know, the replicated reliable queues and dictionaries are the data storage foundations of that framework, where the transactions allow us to do very powerful things.

OK, so before I head on to transactions, I will implement the IPersistentQueue, based on the same principles.

4 Likes

On a more general reflection on this kind of datastructure, I am not entirely comfortable with the idea that every entry in the dictionary would become an immutable data.

It depends on the usecase, but with the usecases I have today in ServiceFabric with IReliableDictionary, it would be a large amount of duplicate data written to the network (since they are used for viewmodels, which are stored again for every event they handle, which could be changing one property out of dozens or more…).

I’m not really sure what we should do about that.

More on…C# Dictionary