Monday 30 March 2020

Redis DataStructure: SortedSet

So let's say that you want to maintain a list of unique items, with items sorted based on the last access time. Few examples of such use case - List of recent actions to be shown against a user profile, List of items in a purchase cart, List of my favorite dishes in a catalog etc.

While there are multiple technology options that can help in this scenario, i wanted to highlight the usage of Redis store for such cases. Redis supports multiple data structures, each serving a set of unique scenario. SortedSet is one such data structure that helps in this particular scenario.

From Redis.IO

Sorted sets are a data type which is similar to a mix between a Set and a Hash. Like sets, sorted sets are composed of unique, non-repeating string elements, so in some sense a sorted set is a set as well.
However while elements inside sets are not ordered, every element in a sorted set is associated with a floating point value, called the score (this is why the type is also similar to a hash, since every element is mapped to a value).
Moreover, elements in a sorted sets are taken in order (so they are not ordered on request, order is a peculiarity of the data structure used to represent sorted sets). They are ordered according to the following rule:
  • If A and B are two elements with a different score, then A > B if A.score is > B.score.
  • If A and B have exactly the same score, then A > B if the A string is lexicographically greater than the B string. A and B strings can't be equal since sorted sets only have unique elements.

I hope things are clear now. Let us get on with a test application code in C# and .NET core.

Let us make an Item class.

public class Item
{
public string Id { get; set; }

public string Name { get; set; }

public string Format()
{
return $"{Id}:{Name}";
}
}

Add a reference to StackExchange.Redis nuget package.

var connection = await ConnectionMultiplexer.ConnectAsync("");
var database = connection.GetDatabase();
string setName = "";

Stopwatch sw = new Stopwatch();

// First insert 20 unique items
sw.Restart();
for (int i = 0; i < 20; i++)
{
Item item = new Item()
{
Id = "90ddc267-e96c-4d48-b05e-43c3cf1e5de1",
Name = "FixedName" + i.ToString()
};
Stopwatch sw1 = new Stopwatch();
sw1.Start();
await database.SortedSetAddAsync(setName, item.Format(), DateTime.UtcNow.Ticks);
sw1.Stop();
Console.WriteLine($"Time taken for 1 SortedSetAddAsync {sw1.ElapsedMilliseconds}");
}

sw.Stop();
Console.WriteLine($"Time taken for 20 SortedSetAddAsync {sw.ElapsedMilliseconds}");

// Try getting top 10 items with their score i.e. access time
var items = await database.SortedSetRangeByRankWithScoresAsync(setName, order: Order.Descending, stop: 9);
Console.WriteLine(items);

// Try overriding items in reverse order
sw.Restart();
for (int i = 19; i >= 0; i--)
{
Item item = new Item()
{
Id = "90ddc267-e96c-4d48-b05e-43c3cf1e5de1",
Name = "FixedName" + i.ToString()
};

Stopwatch sw1 = new Stopwatch();
sw1.Start();
await database.SortedSetAddAsync(setName, item.Format(), DateTime.UtcNow.Ticks);
sw1.Stop();
Console.WriteLine($"Time taken for 1 SortedSetAddAsync {sw1.ElapsedMilliseconds}");
}

sw.Stop();
Console.WriteLine($"Time taken for 20 SortedSetAddAsync {sw.ElapsedMilliseconds}");

// Try getting top 10 items with their score i.e. access time
items = await database.SortedSetRangeByRankWithScoresAsync(setName, order: Order.Descending, stop: 9);
sw.Stop();
Console.WriteLine($"Time taken for top 10 SortedSetRangeByRankWithScoresAsync {sw.ElapsedMilliseconds}");
Console.WriteLine(items);

// Remove items that are not part of top 10 items list
sw.Restart();
await database.SortedSetRemoveRangeByRankAsync(setName, 10, -1);
sw.Stop();
Console.WriteLine($"Time taken for SortedSetRemoveRangeByRankAsync {sw.ElapsedMilliseconds}");

// verify that indeed items have been removed
sw.Restart();
items = await database.SortedSetRangeByRankWithScoresAsync(setName, order: Order.Descending, stop: 9);
sw.Stop();
Console.WriteLine($"Time taken for top 10 SortedSetRangeByRankWithScoresAsync {sw.ElapsedMilliseconds}");
Console.WriteLine(items);


Once you run it, you will realize 2 things.
1. This data structure is quite powerful and handles a lot of concurrency related scenarios out of the box. Less work for you.
2. The operations are fast!! (provided you do not have any major network lag.)

Sunday 29 March 2020

Pro Tip: Killing a windows process

Imagine you are using your beloved windows and working on something important. And suddenly you notice that the "Explorer.exe" is stopped responding - in other words, you are not able to browse folders, programs that open file dialog are hanging now, and you are not able to even open the "Task Manager" from task bar. 

Well, in such cases, one of the options is to "restart" windows :)

But let's say, you want to avoid it because you are not sure if your precious work is saved.

One option you can try is:

1. Open command prompt in admin mode.
2. (optional) Run "tasklist" command to see running processes.
3. Since we know the name of the process that needs to be restarted, we can run taskkill /IM "explorer.exe" /F
4. Now that the previous explorer process is dead, we need to launch a new one. That is simple - run "explorer"


Hope that helps!!