mirror of
https://github.com/redis/go-redis.git
synced 2025-12-03 18:31:14 +03:00
* add disable maintnotifications example * add 8.4-RC1-pre * println -> printf for linter * address jit comment Fix broken initialization of idle connections optimize push notif wip wip wip wip
159 lines
3.5 KiB
Go
159 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
"github.com/redis/go-redis/v9/maintnotifications"
|
|
)
|
|
|
|
func main() {
|
|
ctx := context.Background()
|
|
rdb := redis.NewClient(&redis.Options{
|
|
Addr: ":6379",
|
|
Password: "asdf",
|
|
Username: "default",
|
|
MinIdleConns: 100,
|
|
MaintNotificationsConfig: &maintnotifications.Config{
|
|
Mode: maintnotifications.ModeDisabled,
|
|
},
|
|
})
|
|
commandRunner, stopCommandRunner := NewCommandRunner(rdb)
|
|
defer stopCommandRunner()
|
|
commandRunner.FireCommandsUntilStop(ctx)
|
|
}
|
|
|
|
type CommandRunnerStats struct {
|
|
Operations int64
|
|
Errors int64
|
|
TimeoutErrors int64
|
|
ErrorsList []error
|
|
}
|
|
|
|
// CommandRunner provides utilities for running commands during tests
|
|
type CommandRunner struct {
|
|
client redis.UniversalClient
|
|
stopCh chan struct{}
|
|
operationCount atomic.Int64
|
|
errorCount atomic.Int64
|
|
timeoutErrors atomic.Int64
|
|
errors []error
|
|
errorsMutex sync.Mutex
|
|
}
|
|
|
|
// NewCommandRunner creates a new command runner
|
|
func NewCommandRunner(client redis.UniversalClient) (*CommandRunner, func()) {
|
|
stopCh := make(chan struct{})
|
|
cr := &CommandRunner{
|
|
client: client,
|
|
stopCh: stopCh,
|
|
errors: make([]error, 0),
|
|
}
|
|
return cr, cr.Stop
|
|
}
|
|
|
|
func (cr *CommandRunner) Stop() {
|
|
select {
|
|
case cr.stopCh <- struct{}{}:
|
|
close(cr.stopCh)
|
|
return
|
|
case <-time.After(500 * time.Millisecond):
|
|
return
|
|
}
|
|
}
|
|
|
|
func (cr *CommandRunner) Close() {
|
|
close(cr.stopCh)
|
|
}
|
|
|
|
// FireCommandsUntilStop runs commands continuously until stop signal
|
|
func (cr *CommandRunner) FireCommandsUntilStop(ctx context.Context) {
|
|
fmt.Printf("[CR] Starting command runner...\n")
|
|
defer fmt.Printf("[CR] Command runner stopped\n")
|
|
// High frequency for timeout testing
|
|
ticker := time.NewTicker(100 * time.Millisecond)
|
|
defer ticker.Stop()
|
|
|
|
counter := 0
|
|
for {
|
|
select {
|
|
case <-cr.stopCh:
|
|
return
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
poolSize := cr.client.PoolStats().IdleConns
|
|
if poolSize == 0 {
|
|
poolSize = 1
|
|
}
|
|
wg := sync.WaitGroup{}
|
|
for i := 0; i < int(poolSize); i++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
key := fmt.Sprintf("timeout-test-key-%d-%d", counter, i)
|
|
value := fmt.Sprintf("timeout-test-value-%d-%d", counter, i)
|
|
|
|
// Use a short timeout context for individual operations
|
|
opCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
|
err := cr.client.Set(opCtx, key, value, time.Minute).Err()
|
|
cancel()
|
|
|
|
cr.operationCount.Add(1)
|
|
if err != nil {
|
|
if err == redis.ErrClosed || strings.Contains(err.Error(), "client is closed") {
|
|
select {
|
|
case <-cr.stopCh:
|
|
return
|
|
default:
|
|
}
|
|
return
|
|
}
|
|
|
|
fmt.Printf("Error: %v\n", err)
|
|
cr.errorCount.Add(1)
|
|
|
|
// Check if it's a timeout error
|
|
if isTimeoutError(err) {
|
|
cr.timeoutErrors.Add(1)
|
|
}
|
|
|
|
cr.errorsMutex.Lock()
|
|
cr.errors = append(cr.errors, err)
|
|
cr.errorsMutex.Unlock()
|
|
}
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
counter++
|
|
}
|
|
}
|
|
}
|
|
|
|
func isTimeoutError(err error) bool {
|
|
return strings.Contains(err.Error(), "timeout")
|
|
}
|
|
|
|
// GetStats returns operation statistics
|
|
func (cr *CommandRunner) GetStats() CommandRunnerStats {
|
|
cr.errorsMutex.Lock()
|
|
defer cr.errorsMutex.Unlock()
|
|
|
|
errorList := make([]error, len(cr.errors))
|
|
copy(errorList, cr.errors)
|
|
|
|
stats := CommandRunnerStats{
|
|
Operations: cr.operationCount.Load(),
|
|
Errors: cr.errorCount.Load(),
|
|
TimeoutErrors: cr.timeoutErrors.Load(),
|
|
ErrorsList: errorList,
|
|
}
|
|
|
|
return stats
|
|
}
|