MOVED Error from Redis Cluster(AWS Elasticache) in redis-go

以下の UniversalClient で Redis Cluster(AWS Elasticache) にアクセスしたところ、 "MOVED 1738 127.0.0.1:6379" みたいなエラーが発生した。 www.pospome.work

MOVED というエラー自体は Redis Cluster のもので、アクセス対象のデータを持っているノードへのリダイレクトらしい。 https://redis.io/topics/cluster-spec

このエラーが発生していた原因は go-redis の NewUniversalClient() のパラメータ設定だった。

NewUniversalClient() は引数に指定するノード数によって、 クライアントが Single Redis 用のものなのか Cluster 用のものなのかが決まる。 具体的に言うとノード数が複数の場合は Cluster になり、1つの場合は Single Redis になる。

// NewUniversalClient returns a new multi client. The type of client returned depends
// on the following three conditions:
//
// 1. if a MasterName is passed a sentinel-backed FailoverClient will be returned
// 2. if the number of Addrs is two or more, a ClusterClient will be returned
// 3. otherwise, a single-node redis Client will be returned.
func NewUniversalClient(opts *UniversalOptions) UniversalClient {
    if opts.MasterName != "" {
        return NewFailoverClient(opts.Failover())
    } else if len(opts.Addrs) > 1 {
        return NewClusterClient(opts.Cluster())
    }
    return NewClient(opts.Simple())
}

https://github.com/go-redis/redis/blob/v8.4.0/universal.go#L199


そのため Redis Cluster では以下のようにノードを複数指定することで、Cluster 用のクライアントを生成することになる。

rdb := redis.NewUniversalClient(&redis.UniversalOptions{
    Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},
})


Single Redis はノードが1つしかないので、"ノードを1つのみ指定する = Single Redis である" というのはたしかにそーだが、 AWS の Elasticache Redis Cluster には "Configuration Endpoint" という単一のエンドポイントがあり、 これを利用すればノードを意識せずに Redis を操作することができる。 https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Endpoints.html

なので、Elasticache に関しては以下のように Configuration Endpoint のみ指定していたのだが、 これだと NewUniversalClient() で生成されるクライアントは Single Redis 用のものになってしまい、MOVED Error が発生してしまう。

rdb := redis.NewUniversalClient(&redis.UniversalOptions{
    Addrs: []string{"configuration_endpoint:1234"},
})


とりあえず以下のように同じ Configuration Endpoint を2つ指定することでエラーは発生しなくなった。

rdb := redis.NewUniversalClient(&redis.UniversalOptions{
    Addrs: []string{"configuration_endpoint:1234", "configuration_endpoint:1234"},
})