1
0
mirror of https://github.com/redis/go-redis.git synced 2025-09-05 20:24:00 +03:00

Merge branch 'master' into ndyakov/CAE-1088-resp3-notification-handlers

This commit is contained in:
Nedyalko Dyakov
2025-08-18 22:11:06 +03:00
committed by GitHub
17 changed files with 135 additions and 40 deletions

View File

@@ -25,7 +25,7 @@ runs:
# Mapping of redis version to redis testing containers # Mapping of redis version to redis testing containers
declare -A redis_version_mapping=( declare -A redis_version_mapping=(
["8.2.x"]="8.2" ["8.2.x"]="8.2.1-pre"
["8.0.x"]="8.0.2" ["8.0.x"]="8.0.2"
["7.4.x"]="rs-7.4.0-v5" ["7.4.x"]="rs-7.4.0-v5"
["7.2.x"]="rs-7.2.0-v17" ["7.2.x"]="rs-7.2.0-v17"

View File

@@ -32,7 +32,7 @@ jobs:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Setup Test environment - name: Setup Test environment
env: env:
@@ -44,7 +44,7 @@ jobs:
# Mapping of redis version to redis testing containers # Mapping of redis version to redis testing containers
declare -A redis_version_mapping=( declare -A redis_version_mapping=(
["8.2.x"]="8.2" ["8.2.x"]="8.2.1-pre"
["8.0.x"]="8.0.2" ["8.0.x"]="8.0.2"
["7.4.x"]="rs-7.4.0-v5" ["7.4.x"]="rs-7.4.0-v5"
) )
@@ -84,7 +84,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Run tests - name: Run tests
uses: ./.github/actions/run-tests uses: ./.github/actions/run-tests

View File

@@ -35,7 +35,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL

View File

@@ -36,7 +36,7 @@ jobs:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Test doc examples - name: Test doc examples
working-directory: ./doctests working-directory: ./doctests

View File

@@ -20,7 +20,7 @@ jobs:
name: lint name: lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v8.0.0 uses: golangci/golangci-lint-action@v8.0.0
with: with:

View File

@@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Check Spelling - name: Check Spelling
uses: rojopolis/spellcheck-github-actions@0.51.0 uses: rojopolis/spellcheck-github-actions@0.51.0
with: with:

View File

@@ -20,10 +20,10 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Clone Redis EE docker repository - name: Clone Redis EE docker repository
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
repository: RedisLabs/redis-ee-docker repository: RedisLabs/redis-ee-docker
path: redis-ee path: redis-ee

View File

@@ -301,7 +301,7 @@ func main() {
### Buffer Size Configuration ### Buffer Size Configuration
go-redis uses 0.5MiB read and write buffers by default for optimal performance. For high-throughput applications or large pipelines, you can customize buffer sizes: go-redis uses 32KiB read and write buffers by default for optimal performance. For high-throughput applications or large pipelines, you can customize buffer sizes:
```go ```go
rdb := redis.NewClient(&redis.Options{ rdb := redis.NewClient(&redis.Options{
@@ -376,7 +376,7 @@ You can find further details in the [query dialect documentation](https://redis.
#### Custom buffer sizes #### Custom buffer sizes
Prior to v9.12, the buffer size was the default go value of 4096 bytes. Starting from v9.12, Prior to v9.12, the buffer size was the default go value of 4096 bytes. Starting from v9.12,
go-redis uses 256KiB read and write buffers by default for optimal performance. go-redis uses 32KiB read and write buffers by default for optimal performance.
For high-throughput applications or large pipelines, you can customize buffer sizes: For high-throughput applications or large pipelines, you can customize buffer sizes:
```go ```go

View File

@@ -2412,6 +2412,77 @@ var _ = Describe("Commands", func() {
Expect(args).To(Equal(expectedArgs)) Expect(args).To(Equal(expectedArgs))
}) })
It("should IncrByFloat with edge cases", func() {
// Test with negative increment
set := client.Set(ctx, "key", "10.5", 0)
Expect(set.Err()).NotTo(HaveOccurred())
incrByFloat := client.IncrByFloat(ctx, "key", -2.3)
Expect(incrByFloat.Err()).NotTo(HaveOccurred())
Expect(incrByFloat.Val()).To(BeNumerically("~", 8.2, 0.0001))
// Test with zero increment (should return current value)
incrByFloat = client.IncrByFloat(ctx, "key", 0.0)
Expect(incrByFloat.Err()).NotTo(HaveOccurred())
Expect(incrByFloat.Val()).To(BeNumerically("~", 8.2, 0.0001))
// Test with very small increment (precision test)
incrByFloat = client.IncrByFloat(ctx, "key", 0.0001)
Expect(incrByFloat.Err()).NotTo(HaveOccurred())
Expect(incrByFloat.Val()).To(BeNumerically("~", 8.2001, 0.00001))
// Test with non-existent key (should start from 0)
incrByFloat = client.IncrByFloat(ctx, "nonexistent", 5.5)
Expect(incrByFloat.Err()).NotTo(HaveOccurred())
Expect(incrByFloat.Val()).To(Equal(5.5))
// Test with integer value stored as string
set = client.Set(ctx, "intkey", "42", 0)
Expect(set.Err()).NotTo(HaveOccurred())
incrByFloat = client.IncrByFloat(ctx, "intkey", 0.5)
Expect(incrByFloat.Err()).NotTo(HaveOccurred())
Expect(incrByFloat.Val()).To(Equal(42.5))
// Test with scientific notation
set = client.Set(ctx, "scikey", "1.5e2", 0)
Expect(set.Err()).NotTo(HaveOccurred())
incrByFloat = client.IncrByFloat(ctx, "scikey", 5.0)
Expect(incrByFloat.Err()).NotTo(HaveOccurred())
Expect(incrByFloat.Val()).To(Equal(155.0))
// Test with negative scientific notation
incrByFloat = client.IncrByFloat(ctx, "scikey", -1.5e1)
Expect(incrByFloat.Err()).NotTo(HaveOccurred())
Expect(incrByFloat.Val()).To(Equal(140.0))
// Test error case: non-numeric value
set = client.Set(ctx, "stringkey", "notanumber", 0)
Expect(set.Err()).NotTo(HaveOccurred())
incrByFloat = client.IncrByFloat(ctx, "stringkey", 1.0)
Expect(incrByFloat.Err()).To(HaveOccurred())
Expect(incrByFloat.Err().Error()).To(ContainSubstring("value is not a valid float"))
// Test with very large numbers
set = client.Set(ctx, "largekey", "1.7976931348623157e+308", 0)
Expect(set.Err()).NotTo(HaveOccurred())
// This should work as it's within float64 range
incrByFloat = client.IncrByFloat(ctx, "largekey", -1.0e+308)
Expect(incrByFloat.Err()).NotTo(HaveOccurred())
Expect(incrByFloat.Val()).To(BeNumerically("~", 7.976931348623157e+307, 1e+300))
// Test with very small numbers (near zero)
set = client.Set(ctx, "smallkey", "1e-10", 0)
Expect(set.Err()).NotTo(HaveOccurred())
incrByFloat = client.IncrByFloat(ctx, "smallkey", 1e-10)
Expect(incrByFloat.Err()).NotTo(HaveOccurred())
Expect(incrByFloat.Val()).To(BeNumerically("~", 2e-10, 1e-15))
})
}) })
Describe("hashes", func() { Describe("hashes", func() {

View File

@@ -34,12 +34,12 @@ var _ = Describe("Buffer Size Configuration", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
defer connPool.CloseConn(cn) defer connPool.CloseConn(cn)
// Check that default buffer sizes are used (256KiB) // Check that default buffer sizes are used (32KiB)
writerBufSize := getWriterBufSizeUnsafe(cn) writerBufSize := getWriterBufSizeUnsafe(cn)
readerBufSize := getReaderBufSizeUnsafe(cn) readerBufSize := getReaderBufSizeUnsafe(cn)
Expect(writerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 256KiB buffer size Expect(writerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 32KiB buffer size
Expect(readerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 256KiB buffer size Expect(readerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 32KiB buffer size
}) })
It("should use custom buffer sizes when specified", func() { It("should use custom buffer sizes when specified", func() {
@@ -79,16 +79,16 @@ var _ = Describe("Buffer Size Configuration", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
defer connPool.CloseConn(cn) defer connPool.CloseConn(cn)
// Check that default buffer sizes are used (256KiB) // Check that default buffer sizes are used (32KiB)
writerBufSize := getWriterBufSizeUnsafe(cn) writerBufSize := getWriterBufSizeUnsafe(cn)
readerBufSize := getReaderBufSizeUnsafe(cn) readerBufSize := getReaderBufSizeUnsafe(cn)
Expect(writerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 256KiB buffer size Expect(writerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 32KiB buffer size
Expect(readerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 256KiB buffer size Expect(readerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 32KiB buffer size
}) })
It("should use 256KiB default buffer sizes for standalone NewConn", func() { It("should use 32KiB default buffer sizes for standalone NewConn", func() {
// Test that NewConn (without pool) also uses 256KiB buffers // Test that NewConn (without pool) also uses 32KiB buffers
netConn := newDummyConn() netConn := newDummyConn()
cn := pool.NewConn(netConn) cn := pool.NewConn(netConn)
defer cn.Close() defer cn.Close()
@@ -96,11 +96,11 @@ var _ = Describe("Buffer Size Configuration", func() {
writerBufSize := getWriterBufSizeUnsafe(cn) writerBufSize := getWriterBufSizeUnsafe(cn)
readerBufSize := getReaderBufSizeUnsafe(cn) readerBufSize := getReaderBufSizeUnsafe(cn)
Expect(writerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 256KiB buffer size Expect(writerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 32KiB buffer size
Expect(readerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 256KiB buffer size Expect(readerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 32KiB buffer size
}) })
It("should use 256KiB defaults even when pool is created directly without buffer sizes", func() { It("should use 32KiB defaults even when pool is created directly without buffer sizes", func() {
// Test the scenario where someone creates a pool directly (like in tests) // Test the scenario where someone creates a pool directly (like in tests)
// without setting ReadBufferSize and WriteBufferSize // without setting ReadBufferSize and WriteBufferSize
connPool = pool.NewConnPool(&pool.Options{ connPool = pool.NewConnPool(&pool.Options{
@@ -114,12 +114,12 @@ var _ = Describe("Buffer Size Configuration", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
defer connPool.CloseConn(cn) defer connPool.CloseConn(cn)
// Should still get 256KiB defaults because NewConnPool sets them // Should still get 32KiB defaults because NewConnPool sets them
writerBufSize := getWriterBufSizeUnsafe(cn) writerBufSize := getWriterBufSizeUnsafe(cn)
readerBufSize := getReaderBufSizeUnsafe(cn) readerBufSize := getReaderBufSizeUnsafe(cn)
Expect(writerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 256KiB buffer size Expect(writerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 32KiB buffer size
Expect(readerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 256KiB buffer size Expect(readerBufSize).To(Equal(proto.DefaultBufferSize)) // Default 32KiB buffer size
}) })
}) })

View File

@@ -37,11 +37,11 @@ func NewConnWithBufferSize(netConn net.Conn, readBufSize, writeBufSize int) *Con
createdAt: time.Now(), createdAt: time.Now(),
} }
// Use specified buffer sizes, or fall back to 0.5MiB defaults if 0 // Use specified buffer sizes, or fall back to 32KiB defaults if 0
if readBufSize > 0 { if readBufSize > 0 {
cn.rd = proto.NewReaderSize(netConn, readBufSize) cn.rd = proto.NewReaderSize(netConn, readBufSize)
} else { } else {
cn.rd = proto.NewReader(netConn) // Uses 0.5MiB default cn.rd = proto.NewReader(netConn) // Uses 32KiB default
} }
if writeBufSize > 0 { if writeBufSize > 0 {

View File

@@ -12,8 +12,8 @@ import (
"github.com/redis/go-redis/v9/internal/util" "github.com/redis/go-redis/v9/internal/util"
) )
// DefaultBufferSize is the default size for read/write buffers (256 KiB). // DefaultBufferSize is the default size for read/write buffers (32 KiB).
const DefaultBufferSize = 256 * 1024 const DefaultBufferSize = 32 * 1024
// redis resp protocol data type. // redis resp protocol data type.
const ( const (

View File

@@ -136,14 +136,14 @@ type Options struct {
// Larger buffers can improve performance for commands that return large responses. // Larger buffers can improve performance for commands that return large responses.
// Smaller buffers can improve memory usage for larger pools. // Smaller buffers can improve memory usage for larger pools.
// //
// default: 256KiB (262144 bytes) // default: 32KiB (32768 bytes)
ReadBufferSize int ReadBufferSize int
// WriteBufferSize is the size of the bufio.Writer buffer for each connection. // WriteBufferSize is the size of the bufio.Writer buffer for each connection.
// Larger buffers can improve performance for large pipelines and commands with many arguments. // Larger buffers can improve performance for large pipelines and commands with many arguments.
// Smaller buffers can improve memory usage for larger pools. // Smaller buffers can improve memory usage for larger pools.
// //
// default: 256KiB (262144 bytes) // default: 32KiB (32768 bytes)
WriteBufferSize int WriteBufferSize int
// PoolFIFO type of connection pool. // PoolFIFO type of connection pool.

View File

@@ -96,14 +96,14 @@ type ClusterOptions struct {
// Larger buffers can improve performance for commands that return large responses. // Larger buffers can improve performance for commands that return large responses.
// Smaller buffers can improve memory usage for larger pools. // Smaller buffers can improve memory usage for larger pools.
// //
// default: 256KiB (262144 bytes) // default: 32KiB (32768 bytes)
ReadBufferSize int ReadBufferSize int
// WriteBufferSize is the size of the bufio.Writer buffer for each connection. // WriteBufferSize is the size of the bufio.Writer buffer for each connection.
// Larger buffers can improve performance for large pipelines and commands with many arguments. // Larger buffers can improve performance for large pipelines and commands with many arguments.
// Smaller buffers can improve memory usage for larger pools. // Smaller buffers can improve memory usage for larger pools.
// //
// default: 256KiB (262144 bytes) // default: 32KiB (32768 bytes)
WriteBufferSize int WriteBufferSize int
TLSConfig *tls.Config TLSConfig *tls.Config

View File

@@ -128,14 +128,14 @@ type RingOptions struct {
// Larger buffers can improve performance for commands that return large responses. // Larger buffers can improve performance for commands that return large responses.
// Smaller buffers can improve memory usage for larger pools. // Smaller buffers can improve memory usage for larger pools.
// //
// default: 256KiB (262144 bytes) // default: 32KiB (32768 bytes)
ReadBufferSize int ReadBufferSize int
// WriteBufferSize is the size of the bufio.Writer buffer for each connection. // WriteBufferSize is the size of the bufio.Writer buffer for each connection.
// Larger buffers can improve performance for large pipelines and commands with many arguments. // Larger buffers can improve performance for large pipelines and commands with many arguments.
// Smaller buffers can improve memory usage for larger pools. // Smaller buffers can improve memory usage for larger pools.
// //
// default: 256KiB (262144 bytes) // default: 32KiB (32768 bytes)
WriteBufferSize int WriteBufferSize int
TLSConfig *tls.Config TLSConfig *tls.Config

View File

@@ -97,14 +97,14 @@ type FailoverOptions struct {
// Larger buffers can improve performance for commands that return large responses. // Larger buffers can improve performance for commands that return large responses.
// Smaller buffers can improve memory usage for larger pools. // Smaller buffers can improve memory usage for larger pools.
// //
// default: 256KiB (262144 bytes) // default: 32KiB (32768 bytes)
ReadBufferSize int ReadBufferSize int
// WriteBufferSize is the size of the bufio.Writer buffer for each connection. // WriteBufferSize is the size of the bufio.Writer buffer for each connection.
// Larger buffers can improve performance for large pipelines and commands with many arguments. // Larger buffers can improve performance for large pipelines and commands with many arguments.
// Smaller buffers can improve memory usage for larger pools. // Smaller buffers can improve memory usage for larger pools.
// //
// default: 256KiB (262144 bytes) // default: 32KiB (32768 bytes)
WriteBufferSize int WriteBufferSize int
PoolFIFO bool PoolFIFO bool
@@ -204,8 +204,9 @@ func (opt *FailoverOptions) sentinelOptions(addr string) *Options {
MinRetryBackoff: opt.MinRetryBackoff, MinRetryBackoff: opt.MinRetryBackoff,
MaxRetryBackoff: opt.MaxRetryBackoff, MaxRetryBackoff: opt.MaxRetryBackoff,
ReadBufferSize: opt.ReadBufferSize, // The sentinel client uses a 4KiB read/write buffer size.
WriteBufferSize: opt.WriteBufferSize, ReadBufferSize: 4096,
WriteBufferSize: 4096,
DialTimeout: opt.DialTimeout, DialTimeout: opt.DialTimeout,
ReadTimeout: opt.ReadTimeout, ReadTimeout: opt.ReadTimeout,

View File

@@ -61,6 +61,20 @@ type UniversalOptions struct {
WriteTimeout time.Duration WriteTimeout time.Duration
ContextTimeoutEnabled bool ContextTimeoutEnabled bool
// ReadBufferSize is the size of the bufio.Reader buffer for each connection.
// Larger buffers can improve performance for commands that return large responses.
// Smaller buffers can improve memory usage for larger pools.
//
// default: 32KiB (32768 bytes)
ReadBufferSize int
// WriteBufferSize is the size of the bufio.Writer buffer for each connection.
// Larger buffers can improve performance for large pipelines and commands with many arguments.
// Smaller buffers can improve memory usage for larger pools.
//
// default: 32KiB (32768 bytes)
WriteBufferSize int
// PoolFIFO uses FIFO mode for each node connection pool GET/PUT (default LIFO). // PoolFIFO uses FIFO mode for each node connection pool GET/PUT (default LIFO).
PoolFIFO bool PoolFIFO bool
@@ -143,6 +157,9 @@ func (o *UniversalOptions) Cluster() *ClusterOptions {
WriteTimeout: o.WriteTimeout, WriteTimeout: o.WriteTimeout,
ContextTimeoutEnabled: o.ContextTimeoutEnabled, ContextTimeoutEnabled: o.ContextTimeoutEnabled,
ReadBufferSize: o.ReadBufferSize,
WriteBufferSize: o.WriteBufferSize,
PoolFIFO: o.PoolFIFO, PoolFIFO: o.PoolFIFO,
PoolSize: o.PoolSize, PoolSize: o.PoolSize,
@@ -200,6 +217,9 @@ func (o *UniversalOptions) Failover() *FailoverOptions {
WriteTimeout: o.WriteTimeout, WriteTimeout: o.WriteTimeout,
ContextTimeoutEnabled: o.ContextTimeoutEnabled, ContextTimeoutEnabled: o.ContextTimeoutEnabled,
ReadBufferSize: o.ReadBufferSize,
WriteBufferSize: o.WriteBufferSize,
PoolFIFO: o.PoolFIFO, PoolFIFO: o.PoolFIFO,
PoolSize: o.PoolSize, PoolSize: o.PoolSize,
PoolTimeout: o.PoolTimeout, PoolTimeout: o.PoolTimeout,
@@ -250,6 +270,9 @@ func (o *UniversalOptions) Simple() *Options {
WriteTimeout: o.WriteTimeout, WriteTimeout: o.WriteTimeout,
ContextTimeoutEnabled: o.ContextTimeoutEnabled, ContextTimeoutEnabled: o.ContextTimeoutEnabled,
ReadBufferSize: o.ReadBufferSize,
WriteBufferSize: o.WriteBufferSize,
PoolFIFO: o.PoolFIFO, PoolFIFO: o.PoolFIFO,
PoolSize: o.PoolSize, PoolSize: o.PoolSize,
PoolTimeout: o.PoolTimeout, PoolTimeout: o.PoolTimeout,