Search code examples
govalkey

Making ValKey Client a Global Variable in valkey-go and Gin


In a previous application which used github.com/redis/go-redis/v9 I had var Client *redis.Client available globally with the below setup, instantiated during application start in main.go. This allowed me to instantiate the client once and use the connection throughout my application, along with goroutines for checking it's availability and restarting in the event it was no longer available.

var Client *redis.Client
var Ctx context.Context

func ConnectCache() *redis.Client {

    var redisSentinels = strings.Split(os.Getenv("REDIS_SENTINELS"), ",")
    var redisMaster = os.Getenv("REDIS_MASTER")

    if len(redisSentinels) != 3 {
        panic("Invalid number of sentinels")
    }

    if len(redisMaster) == 0 {
        panic("Invalid redis-master length")
    }

    rdb := redis.NewFailoverClient(&redis.FailoverOptions{
        MasterName:    redisMaster,
        SentinelAddrs: redisSentinels,
        DB:            0,
    })
    return rdb
}

func InitCache() {
    log.Println("Connecting to Cache...")
    Client = ConnectCache()
    Ctx = context.Background()
}

In a new application where I am trying to use github.com/valkey-io/valkey-go instead, I don't seem to be possible. I am using a similar setup (this time without sentinels), but I am only able to utilise the functions exposed through Client within the function in which the client was created.

package cache

import (
    "context"
    "github.com/valkey-io/valkey-go"
    "log"
    "os"
)

var Client *valkey.Client
var Ctx context.Context

func ConnectCache() *valkey.Client {
    var h = os.Getenv("CACHE_HOST")
    var p = os.Getenv("CACHE_PORT")

    client, err := valkey.NewClient(valkey.ClientOption{InitAddress: []string{h + ":" + p}})
    if err != nil {
        log.Printf("Failed to connect to cache: %s", err.Error())
        return nil
    }
    // I can access client.Close() here
    log.Println("Connected to cache")
    return &client
}

func InitCache() {
    log.Println("Initializing cache")
    // This is fine
    Client = ConnectCache()
    Ctx = context.Background()
}

func CloseCache() {
    // I can't access close here
    Client.Close()
}

Does anybody know why this is the case or if something has changes within ValKey to prevent this kind of usage?


Solution

  • Your old code is using var Client *redis.Client where redis.Client is a struct. In your new code you are using var Client *valkey.Client, this differs somewhat because valkey.Client is an interface. This results in an error because you cannot directly access methods on a pointer to an interface.

    The error should have made this fairly clear:

    Client.Close undefined (type *valkey.Client is pointer to interface, not interface)
    

    Firstly consider whether you need a pointer to an interface here, as the FAQ says "Pointers to interface values arise only in rare, tricky situations involving disguising an interface value’s type for delayed evaluation". Changing the definition to var Client valkey.Client (and changing the assignment to match) will solve your issue.

    Alternatively, if you do really need a pointer, you could use (*Client).Close().