Back to blogYouTube Video

Published October 15, 2025

Building a Redis clone in Go - Part 19 (INFO command)

Can't play the video or having issues? Here's the direct link.

AI Summary

This video focuses on implementing the `INFO` command for a Redis clone in Go, which provides detailed server statistics. The author builds the core infrastructure for returning server, client, and memory metrics.

Key Takeaways

  • Implemented an `Info` struct with categorized maps for Server, Client, Memory, Persistence, and General statistics.
  • Integrated the `os` package to retrieve the process ID (`getPID`) and the executable path (`Executable`).
  • Modified the `AppState` to track server start time and the current number of connected clients using `defer` in the connection handler.
  • Utilized the third-party `go-psutil` package to fetch the total system RAM, as Go does not provide a built-in API for this.
  • Added logic to track `peak_memory` by comparing current database memory usage during `SET` operations against a recorded maximum in the `AppState`.

Description

Full Playlist: https://youtube.com/playlist?list=PLTGiYd8gFivgrd_INfVrFDRuBBHfiTxRP&si=lKRTLS38vN4iWqAQ Source Code: https://github.com/hassanaziz0012/go-redis-video LINKS Website: https://www.hassandev.me My Book: https://www.hassandev.me/designing-websites X / Twitter: https://x.com/nothassanaziz

Transcript

Auto-generated transcript
Alright boys, I'm back. In the previous video, we built the monitor command. Down, down, down, down here. Which basically allows other clients to monitor the server and every command that runs on the server. And yeah, we built that in the previous video. In this video, we're going to build the info command. And the info command basically just returns a bunch of information about the server, right? So let's do that. Let's first of all create a new file over here. Call it info.go. Package main. Everything is in package main. Let's create an info struct as well. This is going to have a bunch of categories of different information. So we want to return information about the server. This will be a map of strings. We also want to return information about clients connected to the server. This will be in the client category. We also want to return information about the server memory, like how much memory is being used, how much memory is allowed to be used, and so on. So we'll call this memory. Then some information about data persistence, like RDB and AOF persistence methods. And then finally, just some general stats about the server. Now, let's create a new info function which will return an info struct pointer and return an empty info struct over here. Now let's create a function over here for the info. This will be a pointer receiver and we'll call this build. And this will take the app state as well because we're going to use the app state to get all sorts of different information about the server which we will return in this info struct. So first of all let's just build the server category. This will be a map of strings obviously. String, there we go. First of all we want to return the Redis version. This is the version of Redis that you're using in this server. Now since this is our own custom Redis server we don't really need to return anything over here. So I'm just going to say 1.0.0 right. the initial version of this program. Then we also want to return the process ID. So the system process that is actually running this program, right? And you can actually get that very easily in Go by using the OS package and calling the get PID function. This is going to return a number, an integer. So to convert that to a string, we can call fmt.sprint over here. There we go. And this will basically just convert whatever we give it into its string value, right? Then we can also return the TCP port where this server is running now since this is hard-coded for now we haven't actually added any configuration to the TCP port we can just add the hard-coded port over here as well, which is 6 3 7 9 Then we can also return the server time USEC which basically means the current server time and to get this we can return the time dot now dot Unix micro function call we're using Unix micro instead of Unix right instead of Unix we're using Unix micro because this will return the microseconds not the seconds right and Redis also returns the microseconds in this server time USEC field instead of returning these seconds so obviously convert it into a string as well there we go now we also want to return the uptime in seconds so how long the server has been running in seconds now this is the point where we actually need to use the app state we want to be able to tell when the server actually started right so let open up the app state over here and down here let add a field call it server start which can just be a time dot time There we go And then down here when we create the actual app state we can just say server start equals time dot now Awesome So we create the new app state when we actually start the server right So we can just add a time dot now over here and that will be the time when the server was actually started Then we can use that over here as well You can see simply say time.since, so how much time has passed since this state.server start and then we can just grab the number of seconds from this. This is going to be of type duration or actually not of type float64. So we want to convert this into an integer first of all and now we can convert it to a string. The reason we first convert it into an integer is to just round it off, right? Round it off to the nearest whole number or whatever and just give us a whole number of seconds, an integer of seconds and then we just convert that into a string, right? Next, we want to grab the executable. So, the actual executable path that is running this server. I'll call this exc path and I'll declare it up here and again this is super simple to grab and go we can just say ext path and error is equal to os.executable this is going to return the path name so the path of the executable that is running this program right and obviously then let's handle the error as well if there is an error let's just say that the ext path is equal to an empty string so it doesn't cause any errors for us right then finally let's pass in the config file as well over here and this is going to be the path of the config file that is being used in this server again we can store this in the app state again which is over here we can say something like config file path or actually instead of storing it in the app state we can actually store it in the config struck over here that probably makes a lot more sense right since this is the config struck we can store the config fp over here which will be a string and where do we actually create this let's save this app state as well let's go over to main.go and here we are in the read conf function this is where we actually create the config file so down here once we've actually opened up the file and we know that this file path exists and we can actually successfully open this file right then down here let's just say conf dot um config file path equals fn which is the string and again we could just do this up here as well but then we um where is it but then we don't know whether this file path actually exists or not right so we add it down here after this os dot open call and after we've handled all the errors and stuff so we know that this file exists and is readable right then we just assign the config file path over here and then in the info we can just say something like state.config.conf config fp and that's the path of the config file now for the client there's plenty of different fields that we can return over here but i'm just going to return a single one. I'm going to return the connected clients over here. And again, we're going to need to use the state to actually track this. So I can just say something like client count, which will be an integer And we can track this in main where is it Down here in the handleCon function So let implement this we can say down here We know that when this handle con function is run there a new client that just connected to the server so we can just increment client count over here right and we also know that every time this function ends once this function ends the actual current client um closes the connection so we can just add a defer over here to decrement client count and that's all we need to do to track the number of connections over here and then we can just say state dot um client count and obviously convert it into a string as well there we go and that's all we need to do to return the connected clients and again we can add a bunch of different information to this client category over here but i'm just going to return the connected clients right now for the memory category we're gonna have to use new a different third-party package over here to Basically return a bunch of information about the memory namely just the total amount of memory in this system So let me first of all shut down this server We're going to use This third-party package called go PSU till right you can check it out on github as well It's a very popular package. It has 11 point 5,000 stars. This is a very popular third-party package and it's just going to basically return a bunch of PSUtil functions that you can use. PSUtil just means process utility and it can just basically return a bunch of things like the memory, the CPU and a bunch of other information about system processes, right? The reason I'm grabbing it is to basically get the total amount of system memory right because surprisingly go doesn't give you some sort of a api or whatever or a function to get the total ram in a system so what we can do is say go get and paste in this um url over here this is the package name and just to be completely like just to follow along perfectly fine with me you can get the 4.25.8 version of this package run this it's going to fetch the package from github download it and oops i guess that didn't work oh wait i forgot to add a v over here so this package name at v 4.25.8 do that and it should work this time hopefully or i'm gonna have to record another video for this one okay there we go it's working so we grab this package now let me just clear everything and now we can actually get the total system memory so up here let's say memory and error equals um mem which is over here this package dot virtual memory let me just call this memory so we don't like conflict with the mem keyword over here this is to return a virtual memory stat struct pointer and an error first of all let's just handle the error if error does not equal to nil then we can say the memory total is equal to zero and we can declare a memory total variable up here which will be a positive so an unsigned in 64 right because no system is going to have negative memory right so we can use a unsigned integer over here and i use 64 bits because bytes can be very large if you have something like 32 64 gigs of ram that's going to be a very very huge number in bytes right so make sure to use 64 over here as such and uh if there's an error then the total memory can just be zero otherwise if everything went successfully then you can say mem total equals memory dot total and you can get a bunch of different information about system memory from this virtual memory struct right there a ton of information over here like total memory use memory swap memory reclaimable memory and all sorts of other things we're not going to go through all of them but there's a lot of stuff here that you can use in your own code right we just want the total memory and then we can add it to the memory category in info over here right so let's do this first of all let's just return the current memory that we're using and this is going to be in the database mem field ah there we go and as always convert it into a string by using fmt.sprint then we also want to return the use memory peak right this is going to be the highest amount of memory that we've ever used during the running of this server right so the absolute peak that we've hit in memory usage so again fmt.sprint and we actually need to track this and the way we can track this is in the db.set functions we can first of all go over to db.go over here and in the set function down here we can actually track the memory peak but first of all let's go over to app state and actually add a field over here call it peak memory this will be an inch 64 then let's go back to db.go and down here let's say if the db.memory is greater than state.peakmemory so if we hit a new peak in memory usage then we can say state.peakmemory is equal to db.memory there we go and that's all we need to do save that and then we can just say over here in info state.peakmem Is equal to this use memory peak field then let's return the total system memory as well This is the mem total that we just declared up there, right? and of course convert it into a string and Then let's also return the max memory which will be in our config In state dot con dot max memory and And then also the max memory policy so the eviction policy that we're using for this server Let's say FMT dot sprint or actually we don't need to use FMT dot sprint over here because this is already a string I think there we go or I guess not because this is an eviction type right which under the hood is a string But I guess yeah same thing. So that's all we're going to return for a memory the memory category over here and that's all I'm going to do for this part of the series. We basically created this infrastructure and we added the information for the server, the client and the memory. In the next part of the video or in the next part of this series, I'm going to finish up this info command. We're going to add information for the persistence category and the general category and then we're going to create a handler for the info command and then we'll try running it and see if it works and all of that stuff right. And this This will probably be the final part of this series, right? The next part where we finish this info command. That's probably gonna be the final part of this whole project. So we'll have like 20 videos in this project series and then we'll just end it over there. We built so many features in this project and it's been such a fun time building all of these things, learning all of these new things and all that. But yeah, all good things end and the next part of this series is going to be the end. Thank you for following along all this time with all of these videos. And yeah, thanks for watching and I will see you in the next video, which will be the final part of this series. Stay tuned and see ya.

Share this article

All great things started with a conversation

If you've got a cool project or opportunity and you want me to be a part of it, set up a free meeting with me here, and let's talk. 😊