redis/go-redis

Discovery cluster topology without hardcode

Closed this issue · 2 comments

We use several bare metal nodes to host a redis cluster.

Naturally, not all nodes in the redis cluster use the same port. In order to discovery the cluster topology, at first we used

// hardcode or use a external system to obtain several "seed" node
// I understand not all of them are needed here since there will be a internal discovery later to find all the nodes.
client := redis.NewClusterClient(&redis.ClusterOptions{
	Addrs:  []string{"host1:port1","host1:port2","host2:port1","host2:port2"},
})

We definitely don't want to hardcode. Using an external system is essentially also hardcode since once the client is created the node information is fixed.

A solution I can come up with would be to pass a DNS name into the client and let the client do a DNS SRV resolve every time it needs, which completely eliminate the hardcode. However this functionality is not supported.

Later I tried to implement it as a custom net.Dialer like

func dial(ctx context.Context, network, addr string) (net.Conn, error) {
	nd := &net.Dialer{
		Timeout:   5 * time.Second, // from github.com/redis/go-redis/v9.NewDialer
		KeepAlive: 5 * time.Minute, // from github.com/redis/go-redis/v9.NewDialer
	}

	host, port, err := mypkg.ResolveSRV(addr)
	if err != nil {
		return nil, err
	}

	addr = net.JoinHostPort(host, port)

	return nd.DialContext(ctx, network, addr)
}

and the use the dialer like

client := redis.NewClusterClient(&redis.ClusterOptions{
	Addrs:  []string{"some-reds.internal"},
	Dialer: dial,
})

However, the mypkg.ResolveSRV is not straightforward to implement but needs an understanding of the internal discovery process implementation( I can explain the problem in detail if needed)

So, I wonder if we can implement DNS SRV inside this library, or maybe there are other solutions to avoid hardcode

Hello @rpstw I did wrote a response, but seems i forgot to post it. I do think this is something that should be implemented in the application and not in the client. The client itself provides you a hook (the dialer) where you can resolve the address and the port. Better approach to get the cluster topology would be to use

ClusterSlots func(context.Context) ([]ClusterSlot, error)

ClusterSlots is exactly what we need and thanks.