mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-14 04:07:46 +00:00
- RESP -ERR no longer closes healthy conn / aborts batch; global cap replacement-aware - global health-check pass serialized (latent data race); MemoryMonitor via ResourceManager - token-exchange retry, open-circuit rejections counted as failures - TLS-posture matching transport reuse (no silent InsecureSkipVerify handoff)
80 lines
2.5 KiB
Go
80 lines
2.5 KiB
Go
package backends
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestR177_RedisCommandErrorDoesNotCloseConn regresses the RESP command-error
|
|
// conflation: a Redis '-' (command error) reply, e.g. WRONGTYPE or OOM, is
|
|
// a valid protocol response — it does not mean the connection is dead.
|
|
// Previously Do and Pipeline treated it as an IO error, marking the healthy
|
|
// pooled connection closed (and for pipelines aborting the whole batch),
|
|
// discarding a perfectly reusable connection.
|
|
func TestR177_RedisCommandErrorDoesNotCloseConn(t *testing.T) {
|
|
mr := NewMiniredisServer(t)
|
|
config := &PoolConfig{
|
|
Address: mr.GetAddr(),
|
|
MaxConnections: 5,
|
|
ConnectTimeout: 5 * time.Second,
|
|
ReadTimeout: 3 * time.Second,
|
|
WriteTimeout: 3 * time.Second,
|
|
}
|
|
ctx := context.Background()
|
|
|
|
pool, err := NewConnectionPool(config)
|
|
require.NoError(t, err)
|
|
defer pool.Close()
|
|
|
|
conn, err := pool.Get(ctx)
|
|
require.NoError(t, err)
|
|
defer pool.Put(conn)
|
|
|
|
t.Run("single command error leaves conn healthy", func(t *testing.T) {
|
|
mr.SetError("ERR artificial") // every command returns '-ERR artificial'
|
|
|
|
_, err := conn.Do("GET", "k")
|
|
require.Error(t, err)
|
|
require.True(t, errors.Is(err, ErrCommandReply),
|
|
"expected a Redis command-reply error, got %v", err)
|
|
|
|
mr.SetError("") // clear; the connection must still be reusable
|
|
|
|
// On the buggy path Do marked the conn closed above, so this
|
|
// PING fails with "connection closed"; on the fixed path the
|
|
// conn is untouched and PING succeeds.
|
|
resp, err := conn.Do("PING")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "PONG", resp)
|
|
})
|
|
|
|
t.Run("pipeline command error reads full batch and keeps conn", func(t *testing.T) {
|
|
mr.SetError("ERR artificial")
|
|
|
|
p := conn.NewPipeline()
|
|
p.Queue("SET", "k1", "v1")
|
|
p.Queue("SET", "k2", "v2")
|
|
|
|
// On the buggy path Execute returned after the first error (1
|
|
// response) and closed the conn; on the fixed path all replies
|
|
// are read (healthy conn), the batch is not aborted.
|
|
responses, err := p.Execute()
|
|
require.NoError(t, err, "batch must complete with per-command errors")
|
|
require.Len(t, responses, 2)
|
|
for i, r := range responses {
|
|
er, ok := r.(error)
|
|
require.True(t, ok, "response %d should carry an error value", i)
|
|
require.True(t, errors.Is(er, ErrCommandReply), "response %d should be a command-reply error", i)
|
|
}
|
|
|
|
mr.SetError("")
|
|
resp, err := conn.Do("PING")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "PONG", resp)
|
|
})
|
|
}
|