DEV Community

Bharghava Varun Ayada
Bharghava Varun Ayada

Posted on

Caching records by tags in Go

Introduction

Caching has an important role to play in a system design. It is primarily used to speed up information retrieval from a service and reduce the load on a database engine, the primary source of truth for a service. Data that is frequently requested by users are stored in a in-memory key-value store, like Redis, to speed up subsequent look up of the same data. In this post, I want to specifically talk about cache invalidation and provide an implementation in Go for invalidating cache using tags.

Cache Invalidation

When frequently accessed data which is stored in a cache is updated, it is necessary for the application to evict the stale data from the cache. So when subsequent requests for the same data is received by the application, it will retrieve fresh data from the database and store it in the cache. This way, users will never receive stale data from the application. The process of cache invalidation can be made easier by tagging data with specific tags when storing in the cache. This way, when the data needs to be invalidated, we can use the tag names to evict them from the cache.

Here is an implementation of caching records by tags. We will use Redis and github.com/go-redis/redis package.

import (
    "github.com/go-redis/redis/v7"
)

type cache struct {
    client redis.Client
}

func (c *cache) SetByTags(ctx context.Context, key string, val interface{}, tags []string, expiry time.Duration) error {
    pipe := c.client.TxPipeline()
    for _, tag := range tags {
        pipe.SAdd(tag, key)
        pipe.Expire(tag, expiry)
    }

    pipe.Set(key, b, expiry)

    _, errExec := pipe.Exec()
    return errExec
}

func (c *cache) Invalidate(ctx context.Context, tags []string) {
    keys := make([]string, 0)
    for _, tag := range tags {
        k, _ := c.client.SMembers(tag).Result()
        keys = append(keys, tag)
        keys = append(keys, k...)
    }
    c.client.Del(keys...)
}
Enter fullscreen mode Exit fullscreen mode

In SetByTags method, we maintain a set data structure for each tag provided. We then add the given cache key into these sets. When invalidating cache by tags, we get all the members, i.e. cache keys, for the given tags and delete them from Redis.

Example usage:

tags := []string{"post1", "post2"}
value := "blog data"

key1 := "blog:one:post1"
key2 := "blog:one:post2"

// Set cache by tags here:
cache.SetByTags(ctx, key1, value, tags, 24 * time.Hour)
cache.SetByTags(ctx, key2, value, tags, 24 * time.Hour)


// Invalidate cache by tag "post1" here.
// Both key1 and key2 will be evicted, since both were tagged with "post1".
cache.Invalidate(ctx, []string{"post1"})
Enter fullscreen mode Exit fullscreen mode

References

If you find any issues or errors in the content, please do let me know in the comments below.

In case you like to read more articles related to software development and programming, check out my blog.

Top comments (0)