Redis | Cache user data with Redis in golang app

695 Words
Views

Imagine this. Your fantastic app, written in Golang, is chugging along. You are getting more users and increased activity all the time. Over time, you start hearing complaints about slow performance. This takes you by surprise, and you start exploring potential causes for the slowdown.

The absolute obvious problem you want to fix first is the user behavior tracking system. You want to know most frequently used paths to select code areas for refactoring, simplification, optimization and potential automation.

After digging a bit, you realize that the main culprit is the number of times you have to query the database for fetching and writing data. You track every user request by storing the path and user details in the database. This leads to frequent db updates.

You want to reduce the number of db queries, and you want to do it fast. It’s a good idea to cache the data in memory, and then write it to the database in batches.

A crowd-favorite solution in such a case is redis.

Redis Introduction#

Redis is an open source, in-memory, key-value data store most commonly used as a primary database, cache, message broker, and queue. It delivers sub-millisecond response times, enabling fast and powerful real-time applications.

It integrates within your existing app structure with ease, making it a fantastic caching solution to reduce repeated db fetches and writes when your Golang app starts to scale up.

No wonder it’s the most loved database and also one of the most admired.

For this use case, you will store a list of URLs visited per user with count in redis instead of updating db.

Install Redis for golang#

You will be using go-redis v9 for this little experiment with redis.

Start with

go get github.com/redis/go-redis/v9

There are other clients, for example:

go get github.com/go-redis/redis/v8

Initialize Redis client in golang#

You can use your stored keys from .env to set up a new client:

address := os.Getenv("REDIS_ADDRESS")

if address == "" {

address = "localhost:6379"

}

password := os.Getenv("REDIS_PASSWORD")

db, _ := strconv.Atoi(os.Getenv("REDIS_DB"))

rdb := redis.NewClient(&redis.Options{

Addr: address,

Password: password,

DB: db,

})

This way, you have properly initialized the client for all the environments in use.

As a safety measure, you want to ensure that the client is properly connected to redis before you start using it.

isConnected := true

_, err := rdb.Ping(ctx).Result()

if err != nil {

// Handle Error

isConnected = false

}

Cache user data#

Depending on the app structure, perhaps in auth or route middleware (where you route a user, so you have access to user as well as all the routes requested), you will hook in redis caching logic.

func TrackURLAccess(redisClient *redis.Client, userID string, url string) {
   key := "url_access_count:" + userID

   // Increment the visit count for the URL in the sorted set
   err := redisClient.ZIncrBy(context.Background(), key, 1, url).Err()
   if err != nil {
    log.Println(err)
   }
}

The actual implementation might be slightly different, but basically

ZINCRBY command increments score of a member in the sorted set stored at the key.

The whole cache setup is so easy!

Access information with redis-cli#

You can fetch stored information from cache via redis-cli

> redis-cli //to connect to redis-cli
> GET <key> // get a list

To programmatically access the cached information, you can use:

func GetURLsWithVisitCount(redisClient *redis.Client, userID string) []redis.Z {
   key := "access_count:" + userID

   // Get the URLs with visit counts in descending order
   result, err := redisClient.ZRevRangeWithScores(context.Background(), key, 0, -1).Result()
   if err != nil {
    log.Println(err)
   }

   return result
}

where you use ZRevRange function, and then iterate over the list.

Putting it all together#

func main() {
   // Create the Redis client
   redisClient := createRedisClient()

   // Example usage
   userID := "123"
   url := "https://example.com"

   // Track URL access
   trackURLAccess(redisClient, userID, url)

   // Retrieve the list of URLs with visit counts
   urls := getURLsWithVisitCount(redisClient, userID)

   // Print the URLs and their visit counts
   for _, z := range urls {
    fmt.Printf("URL: %s, Visit Count: %d\n", z.Member, int64(z.Score))
   }
}

It’s a delight to work with redis caching!

Other useful methods while working with redis caching#

  • You can delete the database using FLUSHDB
  • You can delete a specific key using DEL



If you enjoyed this blog post, please


© 2020 - 2023 Aditya Naik