Ctrl K
ComunidadGuías…

internal/home/client_latency_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package home

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
	"testing"
	"testing/synctest"
	"time"

	"github.com/redis/go-redis/v9"
)

func TestSubscriptionTelemetryPreservesHeartbeatDeadline(t *testing.T) {
	for _, test := range []struct {
		name       string
		timeout    time.Duration
		interval   time.Duration
		stale      bool
		heartbeats bool
		traffic    string
	}{
		{name: "default timeout", timeout: homeSubscriptionReceiveTimeout, interval: 30 * time.Second},
		{name: "timeout longer than sampling interval", timeout: 75 * time.Second, interval: 30 * time.Second},
		{name: "stale telemetry", timeout: homeSubscriptionReceiveTimeout, interval: time.Second, stale: true},
		{name: "normal heartbeats renew deadline", timeout: homeSubscriptionReceiveTimeout, interval: time.Second, heartbeats: true},
		{name: "config traffic cannot renew deadline", timeout: 75 * time.Second, interval: 30 * time.Second, traffic: redisChannelConfig},
		{name: "cluster traffic cannot renew deadline", timeout: 75 * time.Second, interval: 30 * time.Second, traffic: redisChannelCluster},
		{name: "unrelated pong cannot renew deadline", timeout: 75 * time.Second, interval: 30 * time.Second, traffic: "unrelated-pong"},
	} {
		t.Run(test.name, func(t *testing.T) {
			synctest.Test(t, func(t *testing.T) {
				local, peer := net.Pipe()
				defer func() {
					if errClose := peer.Close(); errClose != nil {
						t.Error(errClose)
					}
				}()
				go serveRedisCommandTestConn(peer, &redisCommandLog{}, func(args []string) string {
					if strings.EqualFold(args[0], "SUBSCRIBE") {
						return "*3\r\n$9\r\nsubscribe\r\n$6\r\nconfig\r\n:1\r\n"
					}
					return balanceTestRedisReply(args)
				})
				client := redis.NewClient(&redis.Options{
					Addr: "127.0.0.1:8327", Protocol: 2, DisableIdentity: true, MaxRetries: -1,
					Dialer: func(context.Context, string, string) (net.Conn, error) { return local, nil },
				})
				defer func() {
					if errClose := client.Close(); errClose != nil {
						t.Error(errClose)
					}
				}()
				ctx := context.Background()
				pubsub := client.Subscribe(ctx, redisChannelConfig)
				defer func() {
					if errClose := pubsub.Close(); errClose != nil {
						t.Error(errClose)
					}
				}()
				if errACK := receiveSubscriptionACKs(ctx, pubsub, test.timeout, []string{redisChannelConfig}); errACK != nil {
					t.Fatal(errACK)
				}
				sampler := &clusterRTTSampler{history: &clusterLatencyHistory{}, node: clusterNode{IP: "home", Port: 8327}}
				started := time.Now()
				wantElapsed, wantHeartbeats := test.timeout, 0
				if test.heartbeats {
					wantElapsed += 4 * time.Second
					wantHeartbeats = 2
				}
				stop := make(chan struct{})
				defer close(stop)
				go func() {
					timer := time.NewTimer(2 * time.Second)
					defer timer.Stop()
					for {
						select {
						case <-stop:
							return
						case <-timer.C:
						}
						elapsed := time.Since(started)
						if elapsed >= wantElapsed {
							return
						}
						token := ""
						if test.traffic == "" && (!test.heartbeats || (elapsed != 2*time.Second && elapsed != 4*time.Second)) {
							token = sampler.begin(time.Now())
							if test.stale {
								sampler.expire(time.Now(), true)
							}
						}
						frame := fmt.Sprintf("*2\r\n$4\r\npong\r\n$%d\r\n%s\r\n", len(token), token)
						if test.traffic == "unrelated-pong" {
							frame = "*2\r\n$4\r\npong\r\n$5\r\nother\r\n"
						} else if test.traffic != "" {
							frame = fmt.Sprintf("*3\r\n$7\r\nmessage\r\n$%d\r\n%s\r\n$2\r\n{}\r\n", len(test.traffic), test.traffic)
						}
						if _, errWrite := io.WriteString(peer, frame); errWrite != nil {
							// The receiver closes the pipe when its heartbeat deadline expires.
							if !errors.Is(errWrite, io.ErrClosedPipe) && !errors.Is(errWrite, net.ErrClosed) {
								t.Errorf("write telemetry PONG: %v", errWrite)
							}
							return
						}
						timer.Reset(test.interval)
					}
				}()
				heartbeats := 0
				deadline := started.Add(test.timeout)
				for {
					event, errReceive := receiveSubscriptionEvent(ctx, pubsub, sampler, deadline)
					if errReceive != nil {
						if !isTimeoutError(errReceive) {
							t.Fatalf("receive error = %v, want heartbeat timeout", errReceive)
						}
						break
					}
					if test.traffic != "" {
						continue
					}
					pong, ok := event.(*redis.Pong)
					if !ok || pong.Payload != "" {
						t.Fatalf("telemetry escaped to the heartbeat consumer: %#v", event)
					}
					heartbeats++
					deadline = time.Now().Add(test.timeout)
				}
				if elapsed := time.Since(started); elapsed != wantElapsed || heartbeats != wantHeartbeats {
					t.Fatalf("elapsed=%v heartbeats=%d, want %v and %d", elapsed, heartbeats, wantElapsed, wantHeartbeats)
				}
				if samples := sampler.history.report(time.Now()); test.traffic == "" && (len(samples) != 1 || samples[0].Valid == test.stale) {
					t.Fatalf("telemetry was not sampled independently of liveness: %+v", samples)
				}
			})
		})
	}
}