Get started with Velocity
Join the Waitlist
Join Our Discord
Blogs

Cache Data in Redis with Golang

Jeff Vincent
Jeff Vincent
  
January 3, 2024

Learn how to leverage the in-memory data store, Redis, with applications written in Golang to cache data and optimize the performance of your microservice-based application in this post.

Cache Data in Redis with Golang

Redis is an in-memory data store in which you can cache data that your application will likely need again relatively soon. The benefit of caching data in Redis is that the write operation, and the resulting read operation, happens instantaneously — much faster than reading and writing to a persistent data store, such as a traditional database.

Of course, data that is cached in memory is only retained so long as the service responsible for storing the cached data — in this case, Redis — is up, which is what differentiates a service like Redis from, a traditional database like Postgres or MongoDB, which are built to ensure the persistence of data that's been written to them.

Today, we'll build a simple Go app with the API framework Gin to demonstrate connecting to a local Redis instance, which we'll run in Docker, as well as writing data to and reading data from our Redis instance.

Start Redis in Docker

First, we'll need to stand up a Redis instance. For this, we can run the following Docker command, which will start the Redis container and forward traffic to and from Redis' default port, 6379 , to the same port on the running container.

docker run -p 6379:6379 redis

Initialize a new Go project

Before we can start writing Go, we need to create a file called go.mod, which will contain a reference to all of the modules our project will need in order to run.

To do so, we can run the following:

go mod init example.com/m/v2

Then, each time we add import statements to our Go module, we'll need to also run:

go mod tidy

in order to add the new requirements to the go.mod file.

Create main.go

Now that we have our Go project initialized, and we have Redis running locally in Docker, we can define our imports and our main() function in a file called main.go. Specifically, we'll import gin and the redis/v8 engine for building a simple HTTP API and connecting to Redis respectively.

package main

import (
"fmt"
"net/http"

"github.com/gin-gonic/gin"
"github.com/go-redis/redis/v8"
)

var (
redis_host = "127.0.0.1"
redis_port = "6379"
redis_uri  = fmt.Sprintf("redis://%s:%s/0", redis_host, redis_port)
)

func main() {
opt, err := redis.ParseURL(redis_uri)
if err != nil {
panic(err)
}
rdb := redis.NewClient(opt)

router := gin.Default()
router.GET("/ping", func(ctx *gin.Context) {
ping_response := (rdb.Ping(rdb.Context()).Val())
data := map[string]interface{}{
"message": ping_response,
}
ctx.JSONP(http.StatusOK, data)
})
router.Run()
}

Then, we create our Redis URI and pass it to the redis.ParseURL() method, from which we get an object opt that we need to pass to the redis.NewClient() method, which will return a client instance that we can use to interact with Redis.

Next, we can create our first route, “/", which will handle an incoming GET request by sending a “ping” request to Redis. If the Redis instance is accepting connections and is healthy, it will return a “PONG” response.

Before we can run this code, as mentioned above, we'll need to run go mod tidy.

Then, we'll be able to start the app with the following command:

go run main.go

Now, when we go to 127.0.0.1:8080 in the browser, we'll see the following:

Add a route to write to Redis

Now that we can successfully “ping” the Redis instance, we're ready to write some data to it. To do so, we'll add a route to our Gin router called "/write", which will call the function write_cache() when it receives an HTTP GET request.

func write_cache(ctx *gin.Context) {
key := ctx.Query("key")
value := ctx.Query("value")

opt, err := redis.ParseURL(redis_uri)
if err != nil {
panic(err)
}
rdb := redis.NewClient(opt)

_, err = rdb.Set(ctx, key, value, 0).Result()
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}

ctx.JSON(http.StatusOK, gin.H{
"key":   key,
"value": value,
})
}

func main() {
...
router.GET("/write", write_cache)
router.Run()
}

In our new function, we are again creating a Redis client instance, on which we call the Set() method to set the value we parse from the incoming URL as “value” to the corresponding “key” value we parse in the same way.

Redis works by storing “key / value” pairs. A key can be any value, which we can use to request data from Redis. You might think of it as a value by which we can “search.” In our example above, we just called it “key,” but it could be any value. We could have just as easily called it “elephant,” or anything else for that matter.

And likewise, we called the “value” we're passing “value,” but it too could be named anything.

Whatever we call it, that data — when passed to the rib.Set() method, is stored in Redis in the following format: {"key":"value"}.

Try it out:

Restart the app in your terminal, and then try navigating to the following URL in your browser:

127.0.0.1/write?key=1&value=2

You should see the following in the browser:

Add a route to read from Redis

Now that we can write data to Redis, let's create a route that will allow us to “search” or request data that is cached in Redis by the value of the key we passed above.

func read_cache(ctx *gin.Context) {
key := ctx.Query("key")

opt, err := redis.ParseURL(redis_uri)
if err != nil {
panic(err)
}
rdb := redis.NewClient(opt)

value, err := rdb.Get(ctx, key).Result()
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}

ctx.JSON(http.StatusOK, gin.H{
"key":   key,
"value": value,
})
}

func main() {
...
router.GET("/write", write_cache)
router.GET("/read", read_cache)
router.Run()
}

You'll notice that we've followed very much the same pattern here as we did above with the "/write" endpoint. We parse a “key” value from the incoming URL, and we create a Redis client instance. But this time, instead of calling the rib.Set() method, we're calling the rib.Get() method to which we're passing only the key of the key value pair we want to get back from Redis.

Try it out

After adding the above code to you main.go file, go to the following URL in your browser:

127.0.0.1:8080/read?key=1

When you do, you should see the same result we saw last time:

This is because we successfully “searched” Redis for the data stored under the key of 1.

Conclusion

Redis is a powerful tool for caching data that other services in your application will likely need again soon. Above, we walked through the basics of interacting with a Redis instance in Golang via an HTTP API defined with the Gin framework.

Python class called ProcessVideo

Python class called ProcessVideo

Get started with Velocity