P2.8: Redis reconnect/health-check

Connect сохраняет параметры подключения и запускает фоновую проверку (ping каждые
30с). При обрыве Redis-клиент оставался не-nil и записи молча падали в лог; теперь
соединение восстанавливается автоматически через reconnect().
This commit is contained in:
Repinoid
2026-10-06 18:18:06 +03:00
parent 8c39bc5346
commit 07eace3c6b
+60
View File
@@ -31,6 +31,13 @@ import (
// nil означает режим "только память" — все функции тихо no-op.
var Client *redis.Client
// Параметры подключения — сохраняются для переподключения после обрыва.
var (
redisAddr string
redisUsername string
redisPassword string
)
var (
seqMu sync.Mutex
queueWriteSeq = map[string]uint64{}
@@ -53,6 +60,10 @@ func Connect(addr, username, password string) error {
log.Info("persistence: REDIS_ADDR не задан, работаем в memory-only режиме")
return nil
}
redisAddr = addr
redisUsername = username
redisPassword = password
rdb := redis.NewClient(&redis.Options{
Addr: addr,
Username: username,
@@ -66,6 +77,55 @@ func Connect(addr, username, password string) error {
}
Client = rdb
log.Infof("persistence: подключились к Redis %s", addr)
startRedisHealthCheck()
return nil
}
// startRedisHealthCheck — фоновая проверка соединения и переподключение.
// После обрыва Redis-клиент остаётся не-nil, но записи молча падают. Здесь мы
// периодически пингуем Redis и при сбое создаём новый клиент (reconnect), чтобы
// персистентность восстановилась автоматически.
func startRedisHealthCheck() {
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for range ticker.C {
if Client == nil {
continue
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
err := Client.Ping(ctx).Err()
cancel()
if err == nil {
continue
}
log.Warnf("persistence: Redis недоступен (%v), пробуем переподключиться", err)
if reErr := reconnect(); reErr != nil {
log.Errorf("persistence: переподключение не удалось: %v", reErr)
} else {
log.Infof("persistence: переподключились к Redis %s", redisAddr)
}
}
}()
}
// reconnect — создаёт новый Redis-клиент с сохранёнными параметрами подключения.
func reconnect() error {
if redisAddr == "" {
return fmt.Errorf("redis addr пуст")
}
rdb := redis.NewClient(&redis.Options{
Addr: redisAddr,
Username: redisUsername,
Password: redisPassword,
DB: 0,
})
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := rdb.Ping(ctx).Err(); err != nil {
return err
}
Client = rdb
return nil
}