mirror of
https://github.com/redis/go-redis.git
synced 2025-12-02 06:22:31 +03:00
Merge branch 'ndyakov/optional-logger' into optional-logger
This commit is contained in:
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
|||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Test environment
|
- name: Setup Test environment
|
||||||
env:
|
env:
|
||||||
@@ -83,7 +83,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
uses: ./.github/actions/run-tests
|
uses: ./.github/actions/run-tests
|
||||||
|
|||||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
|
|||||||
2
.github/workflows/doctests.yaml
vendored
2
.github/workflows/doctests.yaml
vendored
@@ -36,7 +36,7 @@ jobs:
|
|||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Test doc examples
|
- name: Test doc examples
|
||||||
working-directory: ./doctests
|
working-directory: ./doctests
|
||||||
|
|||||||
4
.github/workflows/golangci-lint.yml
vendored
4
.github/workflows/golangci-lint.yml
vendored
@@ -20,9 +20,9 @@ jobs:
|
|||||||
name: lint
|
name: lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v9.0.0
|
uses: golangci/golangci-lint-action@v9.1.0
|
||||||
with:
|
with:
|
||||||
verify: true
|
verify: true
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/spellcheck.yml
vendored
2
.github/workflows/spellcheck.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
- name: Check Spelling
|
- name: Check Spelling
|
||||||
uses: rojopolis/spellcheck-github-actions@0.54.0
|
uses: rojopolis/spellcheck-github-actions@0.54.0
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
.github/workflows/test-redis-enterprise.yml
vendored
4
.github/workflows/test-redis-enterprise.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Clone Redis EE docker repository
|
- name: Clone Redis EE docker repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: RedisLabs/redis-ee-docker
|
repository: RedisLabs/redis-ee-docker
|
||||||
path: redis-ee
|
path: redis-ee
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ var keylessCommands = map[string]struct{}{
|
|||||||
"sync": {},
|
"sync": {},
|
||||||
"unsubscribe": {},
|
"unsubscribe": {},
|
||||||
"unwatch": {},
|
"unwatch": {},
|
||||||
|
"wait": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
type Cmder interface {
|
type Cmder interface {
|
||||||
|
|||||||
@@ -27,16 +27,17 @@ var (
|
|||||||
errConnNotAvailableForWrite = errors.New("redis: connection not available for write operation")
|
errConnNotAvailableForWrite = errors.New("redis: connection not available for write operation")
|
||||||
)
|
)
|
||||||
|
|
||||||
// getCachedTimeNs returns the current time in nanoseconds from the global cache.
|
// getCachedTimeNs returns the current time in nanoseconds.
|
||||||
// This is updated every 50ms by a background goroutine, avoiding expensive syscalls.
|
// This function previously used a global cache updated by a background goroutine,
|
||||||
// Max staleness: 50ms.
|
// but that caused unnecessary CPU usage when the client was idle (ticker waking up
|
||||||
|
// the scheduler every 50ms). We now use time.Now() directly, which is fast enough
|
||||||
|
// on modern systems (vDSO on Linux) and only adds ~1-2% overhead in extreme
|
||||||
|
// high-concurrency benchmarks while eliminating idle CPU usage.
|
||||||
func getCachedTimeNs() int64 {
|
func getCachedTimeNs() int64 {
|
||||||
return globalTimeCache.nowNs.Load()
|
return time.Now().UnixNano()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCachedTimeNs returns the current time in nanoseconds from the global cache.
|
// GetCachedTimeNs returns the current time in nanoseconds.
|
||||||
// This is updated every 50ms by a background goroutine, avoiding expensive syscalls.
|
|
||||||
// Max staleness: 50ms.
|
|
||||||
// Exported for use by other packages that need fast time access.
|
// Exported for use by other packages that need fast time access.
|
||||||
func GetCachedTimeNs() int64 {
|
func GetCachedTimeNs() int64 {
|
||||||
return getCachedTimeNs()
|
return getCachedTimeNs()
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
package pool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Global time cache updated every 50ms by background goroutine.
|
|
||||||
// This avoids expensive time.Now() syscalls in hot paths like getEffectiveReadTimeout.
|
|
||||||
// Max staleness: 50ms, which is acceptable for timeout deadline checks (timeouts are typically 3-30 seconds).
|
|
||||||
var globalTimeCache struct {
|
|
||||||
nowNs atomic.Int64
|
|
||||||
lock sync.Mutex
|
|
||||||
started bool
|
|
||||||
stop chan struct{}
|
|
||||||
subscribers int32
|
|
||||||
}
|
|
||||||
|
|
||||||
func subscribeToGlobalTimeCache() {
|
|
||||||
globalTimeCache.lock.Lock()
|
|
||||||
globalTimeCache.subscribers += 1
|
|
||||||
globalTimeCache.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func unsubscribeFromGlobalTimeCache() {
|
|
||||||
globalTimeCache.lock.Lock()
|
|
||||||
globalTimeCache.subscribers -= 1
|
|
||||||
globalTimeCache.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func startGlobalTimeCache() {
|
|
||||||
globalTimeCache.lock.Lock()
|
|
||||||
if globalTimeCache.started {
|
|
||||||
globalTimeCache.lock.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
globalTimeCache.started = true
|
|
||||||
globalTimeCache.nowNs.Store(time.Now().UnixNano())
|
|
||||||
globalTimeCache.stop = make(chan struct{})
|
|
||||||
globalTimeCache.lock.Unlock()
|
|
||||||
// Start background updater
|
|
||||||
go func(stopChan chan struct{}) {
|
|
||||||
ticker := time.NewTicker(50 * time.Millisecond)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
for range ticker.C {
|
|
||||||
select {
|
|
||||||
case <-stopChan:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
globalTimeCache.nowNs.Store(time.Now().UnixNano())
|
|
||||||
}
|
|
||||||
}(globalTimeCache.stop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// stopGlobalTimeCache stops the global time cache if there are no subscribers.
|
|
||||||
// This should only be called when the last subscriber is removed.
|
|
||||||
func stopGlobalTimeCache() {
|
|
||||||
globalTimeCache.lock.Lock()
|
|
||||||
if !globalTimeCache.started || globalTimeCache.subscribers > 0 {
|
|
||||||
globalTimeCache.lock.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
globalTimeCache.started = false
|
|
||||||
close(globalTimeCache.stop)
|
|
||||||
globalTimeCache.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
startGlobalTimeCache()
|
|
||||||
}
|
|
||||||
@@ -182,9 +182,6 @@ func NewConnPool(opt *Options) *ConnPool {
|
|||||||
p.connsMu.Unlock()
|
p.connsMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
startGlobalTimeCache()
|
|
||||||
subscribeToGlobalTimeCache()
|
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -985,9 +982,6 @@ func (p *ConnPool) Close() error {
|
|||||||
return ErrClosed
|
return ErrClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
unsubscribeFromGlobalTimeCache()
|
|
||||||
stopGlobalTimeCache()
|
|
||||||
|
|
||||||
var firstErr error
|
var firstErr error
|
||||||
p.connsMu.Lock()
|
p.connsMu.Lock()
|
||||||
for _, cn := range p.conns {
|
for _, cn := range p.conns {
|
||||||
|
|||||||
@@ -4,9 +4,25 @@ set -e
|
|||||||
|
|
||||||
DRY_RUN=1
|
DRY_RUN=1
|
||||||
|
|
||||||
|
helps() {
|
||||||
|
cat <<- EOF
|
||||||
|
Usage: $0 TAGVERSION [-t]
|
||||||
|
|
||||||
|
Creates git tags for public Go packages.
|
||||||
|
|
||||||
|
ARGUMENTS:
|
||||||
|
TAGVERSION Tag version to create, for example v1.0.0
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-t Execute git commands (default: dry run)
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
echo "Error: Tag version is required"
|
echo "Error: Tag version is required"
|
||||||
help
|
helps
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TAG=$1
|
TAG=$1
|
||||||
@@ -24,20 +40,6 @@ while getopts "t" opt; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
help() {
|
|
||||||
cat <<- EOF
|
|
||||||
Usage: $0 TAGVERSION [-t]
|
|
||||||
|
|
||||||
Creates git tags for public Go packages.
|
|
||||||
|
|
||||||
ARGUMENTS:
|
|
||||||
TAGVERSION Tag version to create, for example v1.0.0
|
|
||||||
|
|
||||||
OPTIONS:
|
|
||||||
-t Execute git commands (default: dry run)
|
|
||||||
EOF
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "$DRY_RUN" -eq 1 ]; then
|
if [ "$DRY_RUN" -eq 1 ]; then
|
||||||
echo "Running in dry-run mode"
|
echo "Running in dry-run mode"
|
||||||
|
|||||||
Reference in New Issue
Block a user