You might still remember my previous post when I created a status page using Blazor Server and Redis. In the previous post I mentioned one of the downsides of the implementation which is overeliance to LINQ and Redis.OM, caused by my naive approach to data modelling using Redis.
In this new version, I tried to maximize the use of Redis data structures (List, Set, and Hashes) to better store the service uptime data so it will be easier to store and query.
What's new?
The first version
In the first version, I use Redis.OM to store the data and LINQ to query and format the data. Using this approach the data storage and query quickly became complex, since I tried to model the data as it was a table.
Storing the data:
// save to redis using OMvarcollection=_cnProvider.RedisCollection<MonitoringSnapshot>();awaitcollection.InsertAsync(newMonitoringSnapshot{UnixTimestamp=timestamp.ToUnixSeconds(),ServiceName=serviceName,Healthy=healthy,Latency=latency});
Not that bad, eh?
Querying the data:
// get collectionvarcollection=_cnProvider.RedisCollection<MonitoringSnapshot>();// query all datavarquery=collection.Where(x=>x.UnixTimestamp>nowUnixEpoch).ToList();vartimestamps=query.GroupBy(x=>x.ServiceName).First().OrderBy(x=>x.UnixTimestamp).Select(x=>DateTimeHelpers.FromUnixSeconds((long)x.UnixTimestamp)).ToList();varserviceLatency=query.GroupBy(x=>x.ServiceName).ToDictionary(x=>x.Key,y=>y.OrderBy(i=>i.UnixTimestamp).Select(p=>p.Latency).ToList());
There's so many GroupBy and other commands, not only the querying process is a bit complex, but the stored data is also not easy to scan in RedisInsight.
The new version
In the new version, I tried to use Set, List, and Hash to store the data with my own key pattern. The result is much cleaner and easier data to query.
Storing the data:
// get today datevartimestampDate=timestamp.ToString("yyyy-MM-dd");// save to redis using OMvartimestampKey=string.Format(TimestampKey,serviceName,timestampDate);varlatencyKey=string.Format(LatencyKey,serviceName,timestampDate);varhealthKey=string.Format(HealthKey,serviceName,timestampDate);// get redis dbvardb=_cn.GetDatabase();// set current statusawaitdb.HashSetAsync(ServiceLastStatusKey,serviceName,healthy);// add to setawaitdb.SetAddAsync(ServicesSetKey,serviceName);// add timestamp, health, and latency statusawaitdb.ListRightPushAsync(timestampKey,DateTimeHelpers.ToUnixSeconds(timestamp));awaitdb.ListRightPushAsync(latencyKey,latency);awaitdb.ListRightPushAsync(healthKey,healthy);
Querying the data:
// query all datavartimestampKey=string.Format(TimestampKey,services.First(),nowDate);vartimestampValues=awaitdb.ListRangeAsync(timestampKey);vartimestamps=timestampValues.Select(x=>DateTimeHelpers.FromUnixSeconds(Convert.ToInt64(x))).ToList();// get latency data from all servicesvarlatencyDict=newDictionary<string,List<int>>();foreach(varserviceinservices){// get latency historyvarlatencyKey=string.Format(LatencyKey,service,nowDate);varlatencyHistory=(awaitdb.ListRangeAsync(latencyKey)).Select(x=>Convert.ToInt32(x)).ToList();// add to dictlatencyDict.Add(service,latencyHistory);}
Not only the query logic is much straightforward, the stored data is now much easier to read in RedisInsight. This is a major benefit since you can use other data visualization tools such as Grafana to visualize the collected data.
I've updated the code on my repository and you can simply clone and run it again!
This app started as an idea to integrate to my other project, ritsu-pi, it's a Raspberry Pi home server project where you can deploy all kind of apps to a single RPi (or even a cluster). Redis Hackathon comes in just the right moment to give me an extra motivation to finish this project :)
This project is basically a Status Page (like Github Status, Azure Status, Atlassian Statuspage, or something similar) built on top of Blazor Server and Redis. Here you can define a "health check" and get reports when one of your service is down. Also it has a Discord webhook client that will send you a message when one of your service status has changed.
Monitor HTTP/TCP service uptime
Simple latency graph over time
Incident report when one of the services status has changed