RedisJSON with Golang

Recently, I've been working on an authentication system where multiple services push user information through RabbitMQ and a consumer store record into the MongoDB database.

To reduce the frequent requests to the database, redis cache would be a better fit but I had very basic knowledge about redis. I knew that it stores information in key value.

As MongoDB schema design all the information sent by services will be stored in JSON format. So, I need to store JSON data in redis.

After surfing in internet I came to know that redis has a module RedisJSON which provides JSON support for Redis.Since, I'm using Golang to write service, found go-rejson package which supports write and read JSON in redis.

These are the steps that I try to Implementation RedisJSON in Golang. You can find code at Github Repo

step 1 Setup a Redis Server in Docker

docker run -n redis -p 6379:6379 redislabs/redismod

You can connect to redis server with redis-cli

step 2 Verify RedisJSON module in redis Server

#1
docker exec -it (container name / id )) bash 

#2
redis-cli

#3
info moudles

Check modules with Name rejson

Step 3 Initialise Go module

go mod init github/prabeshmagar/go-redis-json

Example with Go-redis

Go-redis is a type-safe, Redis client library for Go with support for features like Pub/Sub, sentinel, and pipelining.It is a Redis client able to support a Redis cluster and is designed to store and update slot info automatically with a cluster change.

The go-redis library is located in the https://github.com/go-redis/redis.

step 1 Install go-redis

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

step 2 Write goredis service

package service

import (
    "bytes"
    "context"
    "encoding/gob"
    "fmt"
    "log"

    "github.com/go-redis/redis/v8"
    "github.com/nitishm/go-rejson/v4"
)

type GoRedis struct {
    Client  *redis.Client
    Handler *rejson.Handler
}

func GoRedisExample(url string) *GoRedis {
    client := redis.NewClient(&redis.Options{
        Addr: url,
    })

    rh := rejson.NewReJSONHandler()
    rh.SetGoRedisClient(client)

    return &GoRedis{
        Client:  client,
        Handler: rh,
    }
}

func (r GoRedis) GoRedisSetJSON(key string, value interface{}) error {
    res, err := r.Handler.JSONSet(key, ".", value)
    if err != nil {
        log.Fatalf("Failed to JSONSet with go redis")
        return err
    }

    if res.(string) == "OK" {
        fmt.Printf("GoRedis Success: %s\n", res)
    }
    return nil
}

func (r GoRedis) GoRedisGetJSON(key string) {
    var buf bytes.Buffer
    enc := gob.NewEncoder(&buf)

    ctx := context.Background()

    val, err := r.Client.Do(ctx, "JSON.GET", key, ".name").Result()

    if err != nil {
        fmt.Println("unable to get data from redis")
    }

    er := enc.Encode(val)

    if er != nil {
        panic("unable to encode value")
    }

    fmt.Printf("Reading data from redis with goredis : %#v\n", val)
}

step 3 Main code

package main

import (
    "encoding/json"
    "fmt"
    "log"

    "github.com/prabeshmagar/go-redis-json/service"
)

var redisHost = "localhost:6379"

func main() {
    
    anotherStudent := service.Student{
        Name: service.Name{
            First:  "Prabesh",
            Middle: "",
            Last:   "Magar",
        },
        Rank: 2,
    }

    // Goredis example
    goRedisService := service.GoRedisExample(redisHost)

    err = goRedisService.GoRedisSetJSON("another", anotherStudent)

    if err != nil {
        fmt.Println("failed to store json in redis with goredis")
    }

    goRedisService.GoRedisGetJSON("another")
}

Output

<img src="go-redis-out.png" style="width:100%; height:auto;>

Example with redigo

Redigo is a Go client for the Redis database. Features. A Print-like API with support for all Redis commands. Pipelining, including pipelined transactions. Publish/Subscribe. Connection pooling. Script helper type with optimistic use of EVALSHA. Helper functions for working with command replies.

The redigo library is located in the https://github.com/gomodule/redigo

step 1 Install redigo

 go get github.com/gomodule/redigo/redis

step 2 Write redigo service

package service

import (
    "fmt"
    "log"

    "github.com/gomodule/redigo/redis"
    "github.com/nitishm/go-rejson/v4"
)

// Name - student name
type Name struct {
    First  string `json:"first,omitempty"`
    Middle string `json:"middle,omitempty"`
    Last   string `json:"last,omitempty"`
}

// Student - student object
type Student struct {
    Name Name `json:"name,omitempty"`
    Rank int  `json:"rank,omitempty"`
}

type RedigoService struct {
    Client  redis.Conn
    Handler *rejson.Handler
}

func RedigoExample(url string) RedigoService {
    conn, err := redis.Dial("tcp", url)
    rh := rejson.NewReJSONHandler()

    if err != nil {
        log.Fatalf("Failed to connect to redis-server @ %s", url)
        panic(err)
    }

    rh.SetRedigoClient(conn)

    return RedigoService{
        Client:  conn,
        Handler: rh,
    }
}

func (r RedigoService) SetJson(key string, value interface{}) error {
    res, err := r.Handler.JSONSet(key, ".", value)
    if err != nil {
        log.Fatalf("Failed to JSONSet")
        return err
    }

    if res.(string) == "OK" {
        fmt.Printf("Redigo Success: %s\n", res)
    }
    return nil
}

func (r RedigoService) GetJson(key string) []byte {

    studentJSON, err := redis.Bytes(r.Handler.JSONGet(key, "."))
    if err != nil {
        log.Fatalf("Failed to JSONGet")
        return nil
    }

    return studentJSON
}

Application code with Redigo

package main

import (
    "encoding/json"
    "fmt"
    "log"

    "github.com/prabeshmagar/go-redis-json/service"
)

var redisHost = "localhost:6379"

func main() {
    student := service.Student{
        Name: service.Name{
            First:  "Mark",
            Middle: "S",
            Last:   "Pronto",
        },
        Rank: 1,
    }
    //RedigoExample
    redigoService := service.RedigoExample(redisHost)

    err := redigoService.SetJson("student", student)

    if err != nil {
        fmt.Println("failed to store json in redis with redigo")
    }

    studentJSON := redigoService.GetJson("student")

    readStudent := service.Student{}

    err = json.Unmarshal(studentJSON, &readStudent)
    if err != nil {
        log.Fatalf("Failed to JSON Unmarshal")
    }

    fmt.Printf("Reading data from redis with redigo : %#v\n", readStudent)
}

Output

Conclusion

This article explain about working with json in Redis using Golang and redis package redigo and go-redis and go-rejson package.