Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Redis Cluster Configuration Endpoint #4947

Merged
merged 1 commit into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/sources/configuration/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1757,7 +1757,7 @@ memcached_client:
[consistent_hash: <bool>]

redis:
# Redis Server endpoint to use for caching. A comma-separated list of endpoints
# Redis Server or Cluster configuration endpoint to use for caching. A comma-separated list of endpoints
# for Redis Cluster or Redis Sentinel. If empty, no redis will be used.
# CLI flag: -<prefix>.redis.endpoint
[endpoint: <string>]
Expand Down
7 changes: 6 additions & 1 deletion pkg/storage/chunk/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"flag"
"fmt"
"time"

"github.com/go-kit/log"
Expand Down Expand Up @@ -113,7 +114,11 @@ func New(cfg Config, reg prometheus.Registerer, logger log.Logger) (Cache, error
cfg.Redis.Expiration = cfg.DefaultValidity
}
cacheName := cfg.Prefix + "redis"
cache := NewRedisCache(cacheName, NewRedisClient(&cfg.Redis), logger)
client, err := NewRedisClient(&cfg.Redis)
if err != nil {
return nil, fmt.Errorf("redis client setup failed: %w", err)
}
cache := NewRedisCache(cacheName, client, logger)
caches = append(caches, NewBackground(cacheName, cfg.Background, Instrument(cacheName, cache, reg), reg))
}

Expand Down
27 changes: 23 additions & 4 deletions pkg/storage/chunk/cache/redis_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/tls"
"flag"
"fmt"
"net"
"strings"
"time"
"unsafe"
Expand All @@ -31,7 +32,7 @@ type RedisConfig struct {

// RegisterFlagsWithPrefix adds the flags required to config this to the given FlagSet
func (cfg *RedisConfig) RegisterFlagsWithPrefix(prefix, description string, f *flag.FlagSet) {
f.StringVar(&cfg.Endpoint, prefix+"redis.endpoint", "", description+"Redis Server endpoint to use for caching. A comma-separated list of endpoints for Redis Cluster or Redis Sentinel. If empty, no redis will be used.")
f.StringVar(&cfg.Endpoint, prefix+"redis.endpoint", "", description+"Redis Server or Cluster configuration endpoint to use for caching. A comma-separated list of endpoints for Redis Cluster or Redis Sentinel. If empty, no redis will be used.")
f.StringVar(&cfg.MasterName, prefix+"redis.master-name", "", description+"Redis Sentinel master name. An empty string for Redis Server or Redis Cluster.")
f.DurationVar(&cfg.Timeout, prefix+"redis.timeout", 500*time.Millisecond, description+"Maximum time to wait before giving up on redis requests.")
f.DurationVar(&cfg.Expiration, prefix+"redis.expiration", 0, description+"How long keys stay in the redis.")
Expand All @@ -51,9 +52,27 @@ type RedisClient struct {
}

// NewRedisClient creates Redis client
func NewRedisClient(cfg *RedisConfig) *RedisClient {
func NewRedisClient(cfg *RedisConfig) (*RedisClient, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think would be nice to add few simples tests to lock this behaviour in NewRedisClient with different values for cfg.Endpoint (single vs multiple comma separated strings?)

We may not need to patch Go resolver during runtime (like you mentioned on the PR description). How about just passing list comma separated IPs and not hostnames? Will that help?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have 2 tests here which pass a single endpoint and then 2 endpoints as a cluster using mock redis instances: https://github.com/grafana/loki/blob/main/pkg/storage/chunk/cache/redis_client_test.go#L67-L100
I assume these are passing IPs to the client config, so they should already cover what you mentioned.

endpoints := strings.Split(cfg.Endpoint, ",")
// Handle single configuration endpoint which resolves multiple nodes.
if len(endpoints) == 1 {
host, port, err := net.SplitHostPort(endpoints[0])
if err != nil {
return nil, err
}
addrs, err := net.LookupHost(host)
if err != nil {
return nil, err
}
if len(addrs) > 1 {
endpoints = nil
for _, addr := range addrs {
endpoints = append(endpoints, net.JoinHostPort(addr, port))
}
}
}
opt := &redis.UniversalOptions{
Addrs: strings.Split(cfg.Endpoint, ","),
Addrs: endpoints,
MasterName: cfg.MasterName,
Password: cfg.Password.Value,
DB: cfg.DB,
Expand All @@ -68,7 +87,7 @@ func NewRedisClient(cfg *RedisConfig) *RedisClient {
expiration: cfg.Expiration,
timeout: cfg.Timeout,
rdb: redis.NewUniversalClient(opt),
}
}, nil
}

func (c *RedisClient) Ping(ctx context.Context) error {
Expand Down