ServiceStack.Net Redis: Storing Related Objects vs. Related Object Ids

Rather than re-hash a lot of other documentation that’s out there in the wild, I’ll list a couple around for some background info around Redis + ServiceStack’s Redis Client:

There is no magic – Redis is a blank canvas

First I want to point out that using Redis as a data store just provides a blank canvas and doesn’t have any concept of related entities by itself. i.e. it just provides access to distributed comp-sci data structures. How relationships get stored is ultimately up to the client driver (i.e. ServiceStack C# Redis Client) or the app developer, by using Redis’s primitive data structure operations. Since all the major data structures are implemented in Redis, you basically have complete freedom on how you want to structure and store your data.

Think how you would structure relationships in code

So the best way to think about how to store stuff in Redis, is to completely disregard about how data is stored in an RDBMS table and think about how it is stored in your code, i.e. using the built-in C# collection classes in memory – which Redis mirrors in behavior with their server-side data-structures.

Despite not having a concept of related entities, Redis’s built-in Set and SortedSet data structures provide the ideal way to store indexes. E.g. Redis’s Set collection only stores a max of 1 occurrence of an element. This means you can safely add items/keys/ids to it and not care if the item exists already as the end result will be the same had you called it 1 or 100 times – i.e. it’s idempotent, and ultimately only 1 element remains stored in the Set. So a common use-case is when storing an object graph (aggregate root) is to store the Child Entity Ids (aka Foreign Keys) into a Set every time you save the model.

Visualizing your data

For a good visualization of how Entities are stored in Redis I recommend installing the Redis Admin UI which works well with ServiceStack’s C# Redis Client as it uses the key naming convention below to provide a nice hierarchical view, grouping your typed entities together (despite all keys existing in the same global keyspace).

To view and edit an Entity, click on the Edit link to see and modify the selected entity’s internal JSON representation. Hopefully you’ll be able to make better decisions about how to design your models once you can see how they’re stored.

How POCO / Entities are stored

The C# Redis Client works with any POCOs that have a single primary key – which by default is expected to be Id (though this convention overridable with ModelConfig).
Essentially POCOs gets stored into Redis as serialized JSON with both the typeof(Poco).Name and the Id used to form a unique key for that instance. E.g:

urn:Poco:{Id} => '{"Id":1,"Foo":"Bar"}'

POCOs in the C# Client are conventionally serialized using ServiceStack’s fast Json Serializer where only properties with public getters are serialized (and public setters to get de-serialized back).

Defaults are overrideable with [DataMember] attrs but not recommended since it uglifies your POCOs.

Entities are blobbed

So knowing that POCOs in Redis are just blobbed, you only want to keep non-aggregate root data on your POCOs as public properties (unless you purposely want to store redundant data). A good convention is to use methods to fetch the related data (since it wont get serialized) but also tells your app which methods make remote calls to read data.

So the question on whether the Feed should get stored with the User is whether or not it’s non-aggregate root data, i.e. whether or not you want to access the users feeds outside the context of the user? If no, then leave the List<Feed> Feeds property on the User type.

Maintaining Custom Indexes

If however you would like to keep all feeds accessible independently, i.e. with redisFeeds.GetById(1) then you will want to store it outside of the user and maintain an index linking the 2 entities.

As you’ve noticed there are many ways to store relationships between entities and how you do so is largely a matter of preference. For the child entity in a parent>child relationship you would always want to store the ParentId with the child entity. For the Parent you can either choose to store a collection of ChildIds with the model and then do a single fetch for all child entities to re-hydrate the model.

Another way is to maintain the index outside of the parent dto in its own Set for each parent instance. Some good examples of this is in the C# Source code of the Redis StackOverflow demo where the relationship of Users > Questions and Users > Answers is stored in:

idx:user>q:{UserId} => [{QuestionId1},{QuestionId2},etc]
idx:user>a:{UserId} => [{AnswerId1},{AnswerId2},etc]

Although the C# RedisClient does include support for a default Parent/Child convention via its TParent.StoreRelatedEntities(), TParent.GetRelatedEntities<TChild>() and TParent.DeleteRelatedEntities() APIs where an index is maintained behind the scene that looks like:

ref:Question/Answer:{QuestionId} => [{answerIds},..]

Effectively these are just some of your possible options, where there are many different ways to achieve the same end and in which you also have the freedom to roll your own.

NoSQL’s schema-less, loose-typing freedoms should be embraced and you shouldn’t be worried about trying to follow a rigid, pre-defined structure you might be familiar with when using an RDBMS.

In conclusion, there’s no real right way to store data in Redis, e.g. The C# Redis Client makes some assumptions in order to provide a high-level API around POCOs and it blobs the POCOs in Redis’s binary-safe string values – though there are other clients will prefer to store an entities properties in Redis Hashes (Dictionaries) instead. Both will work.

Leave a Comment