Working With List on Redis With .Net 6.0

Bora Kaşmer
Geek Culture
Published in
7 min readMay 20, 2022

--

The Brick-Making Women of India(National Geographic)

Today we will talk about how to work with cumulative data in Redis. First of all, Redis is not a cache manager Tool. It is an In-Memory DB. Redis means “Remote Dictionary Service”. There are five different data types in Redis. Today we will talk about “Lists”. But in the end we will have to change our Redis Data Type for a new busines rules.

Scenario: We have several different categories. A new one can be added and any of them can be removed. The category model seems as below. We will save all categories on Redis to improve our service’s performance.

public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public int ParentID { get; set; }
public DateTime CreateDate { get; set; }
}

We will use ServiceStack Library for Redis in .Net 6.0

First Method:

When I checked the junior developer’s code, I saw, that he set Category List to the Redis as JSON data with Key-Value pair. When he adds a new item to the Category List, he gets JSON data, deserializes it adds a new category item to the list then serialize again and set List to the Redis as JSON. This is unacceptable. For big data, this is a performance killer.

*This method is similar to taking off all books from the library, adding the new book, and putting all taken books back into the library again :)

CategoryList Keeps in Redis as JSON
  1. We created some dummy categories and added to “categoryList”
Dummy Category Items

2. We set Redis connection server and got Redis Client.

3. We set “CategoryList” to the Redis as JSON as seen above. And got “CategoryList” again.

Set Current CategoryList to the Redis. And Got It back.

4. We removed the Category Id=2, added new Category Id=4 and finally set “CategoryList” to the Redis as JSON again.

Remove “Category2” Add “CategoryBora”

5. Finally we got all “CategoryList” from Redis and printed them all in a loop as seen below.

Print All CategoryList to the Screen

Program.cs:

List<Category> categoryList = new();categoryList.AddRange(new List<Category>() {
new Category() { Id=1,Name="Category1",CreateDate=DateTime.Now},
new Category() { Id=2,Name="Category2",CreateDate=DateTime.Now},
new Category() { Id=3,Name="Category3",CreateDate=DateTime.Now}});
RedisEndpoint conf = new RedisEndpoint { Host = "127.0.0.1", Port = 6379, Password = "test" };RedisClient redisClient = new RedisClient(conf);redisClient.Set("CategoryList", categoryList);var list = redisClient.Get<List<Category>>("CategoryList");list.Remove(list.First(item => item.Id == 2));list.Add(new Category() { Name = "CategoryBora", Id = 4, CreateDate = DateTime.Now });redisClient.Set("CategoryList", list);var finalList = redisClient.Get<List<Category>>("CategoryList");foreach (var item in finalList)
{
Console.WriteLine(JsonSerializer.Serialize(item));
}
Result Screen

Second Method:

Now it is time to give a chance to “Lists Data Type” in Redis. We don’t have to deal with the all data in Redis when we add, update or remove an item from the list. Dealing with only one item, could be improve the performance.

CategoryList Keeps in Redis as List
  1. We created “CategoryList” on Redis. The type of List is “Category”. It is like “List<Category>”.
Redis Lists[“CategoryList”]

2. We Bulk Inserted all Categories to “CategoryList” on Redis. And finally added a new “Category4” without touching to Current List :)

Bulk Insert CategoryList and Add Category4

3. Removing an Item from the Redis List is very painful :) You have to find the Index of the item which will be removed. I got (value, index) with the “Single” filter keyword which “Id==3"

I took removedCategory, change the name to “CategoryBora” and put it back to “CategoryList” with the same Index number.

Not:(removedCategory.Index-1) Because one item is removed so “current Index number” => 1 dropped.

Remove Category 3, Change Name and Put It Back

4. We got “CategoryList” from the Redis. And we printed all items in a loop as seen below.

Program.cs:

List<Category> categoryList = new();categoryList.AddRange(new List<Category>() {
new Category() { Id=1,Name="Category1",CreateDate=DateTime.Now},
new Category() { Id=2,Name="Category2",CreateDate=DateTime.Now},
new Category() { Id=3,Name="Category3",CreateDate=DateTime.Now}});
RedisEndpoint conf = new RedisEndpoint { Host = "127.0.0.1", Port = 6379, Password = "test" };RedisClient redisClient = new RedisClient(conf);//Create CategoryList on Redis and add all items to the list..
IRedisTypedClient<Category> categoryClient = redisClient.As<Category>();
var categoryRedisList = categoryClient.Lists["CategoryList"];categoryRedisList.AddRange(categoryList);//Add new Category4 to the CategoryList
categoryRedisList.Add(new Category() { Id = 4, Name = "Category4", CreateDate = DateTime.Now });
//Remove "Category Id=3" from the List with it's Index.
var removeCategory = categoryRedisList.Select((Value, Index) => new { Value, Index }).Single(p => p.Value.Id == 3);
categoryRedisList.RemoveAt(removeCategory.Index);//Change the Name of Removed Category and put it back
removeCategory.Value.Name = "CategoryBora";
categoryRedisList.Insert(removeCategory.Index - 1, removeCategory.Value);
//Get the CategoryList from the redis and print all items in a loop
var getCategoryList = categoryRedisList.GetAll();
getCategoryList.ForEach(item =>{
Console.WriteLine(JsonSerializer.Serialize(item));
});
Result Screen

Third Method:

Later, we got a new case from the customer. “Every category must have an expiration time.” So the current Redis Lists Data type solution has become useless at the end. Because in the List, we can not set an expiration time for each of the category. Only we can set an expiration time for the group of category in Redis Lists. So Bulk expiration time was not acceptable for the customer. At the end, last time we changed our Data Type in Redis.

Every Category keeps in Category Group

1-) We decided to grow vertically in Redis. We added all categories one by one under the “Category:” keyword with their Ids. “Category” is the name of the group and “id” is the unique identity of every category.

“Category:{item.Id}

Set All Categories Under The Category: Keyword.

2-) We add a new “Category4” under the “Category:” keyword with its “4” Id.

Add “Category4” under The “Category:”

3-) We easily found “Category3” by its keyword. Against the Lists data type, we don’t need any Index of the item for removing it. We changed the name of “Category3” with “Bora” and put it back by using it’s “Category:3” keyword.

We Found “Category3” And Update It

4-) We select all Categories with the “Category*” keyword. We call it Lua Scripting. We filtered all Redis Items, which is starting with the “Category” keyword. We checked if there were any items with the start “Category” keyword. If the result is yes, we got all of them and ordered by their “Id”. And finally, we printed every one of them into the screen.

All Categories Printed One By One

Program.cs:

List<Category> categoryList = new();categoryList.AddRange(new List<Category>() {
new Category() { Id=1,Name="Category1",CreateDate=DateTime.Now},
new Category() { Id=2,Name="Category2",CreateDate=DateTime.Now},
new Category() { Id=3,Name="Category3",CreateDate=DateTime.Now}});
RedisEndpoint conf = new RedisEndpoint { Host = "127.0.0.1", Port = 6379, Password = "test" };RedisClient redisClient = new RedisClient(conf);//We added all categories under the "Category:" with theirs Ids.
categoryList.ForEach(item =>
{
redisClient.Add($"Category:{item.Id}", item);
});
//We add new "Category4" to under the "Category:" keyword.
redisClient.Add("Category:4", new Category() { Id = 4, Name = "Category4", CreateDate = DateTime.Now });
//We Found “Category3” And Update It.
var category3 = redisClient.Get<Category>("Category:3");
category3.Name = "Bora";
redisClient.Set("Category:3", category3);
//All Categories found with "Category*" keyword and printed one by one to the screen.
var keys = redisClient.SearchKeys($"Category*");
if (keys.Any())
{
List<Category> getAllCategoryList = redisClient.GetAll<Category>(keys).Values.OrderBy(item => item.Id).ToList();
getAllCategoryList.ForEach(item =>
{
Console.WriteLine(JsonSerializer.Serialize(item));
});
}
Result Screen

Conclusion:

Redis is not the only key/value memory management tool. It is an In-Memory DB. You can keep your data in several ways in Redis. Growing vertically is not a performance problem for Redis. But keeping Big data as Json in Redis could be problem when high traffic. Serialize and Deserialize are high cost operations. What I am trying to explain with this article is, “If you spliting your data and puting them under a group with their unique identity, you could rid of your performance problem”

See you later until the next article. Bye.

“If you have read so far, first of all, thank you for your patience and support. I welcome all of you to my blog for more!”

Source:

--

--

Bora Kaşmer
Geek Culture

I have been coding since 1993. I am computer and civil engineer. Microsoft MVP. Software Architect(Cyber Security). https://www.linkedin.com/in/borakasmer/