mirror of
https://github.com/redis/go-redis.git
synced 2025-12-02 06:22:31 +03:00
try to cache time if for non-critical calculation
This commit is contained in:
@@ -18,6 +18,35 @@ import (
|
||||
|
||||
var noDeadline = time.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
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Initialize immediately
|
||||
globalTimeCache.nowNs.Store(time.Now().UnixNano())
|
||||
|
||||
// Start background updater
|
||||
go func() {
|
||||
ticker := time.NewTicker(50 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
globalTimeCache.nowNs.Store(time.Now().UnixNano())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// getCachedTimeNs returns the current time in nanoseconds from the global cache.
|
||||
// This is updated every 50ms by a background goroutine, avoiding expensive syscalls.
|
||||
// Max staleness: 50ms.
|
||||
func getCachedTimeNs() int64 {
|
||||
return globalTimeCache.nowNs.Load()
|
||||
}
|
||||
|
||||
// Global atomic counter for connection IDs
|
||||
var connIDCounter uint64
|
||||
|
||||
@@ -458,7 +487,8 @@ func (cn *Conn) getEffectiveReadTimeout(normalTimeout time.Duration) time.Durati
|
||||
return time.Duration(readTimeoutNs)
|
||||
}
|
||||
|
||||
nowNs := time.Now().UnixNano()
|
||||
// Use cached time to avoid expensive syscall (max 50ms staleness is acceptable for timeout checks)
|
||||
nowNs := getCachedTimeNs()
|
||||
// Check if deadline has passed
|
||||
if nowNs < deadlineNs {
|
||||
// Deadline is in the future, use relaxed timeout
|
||||
@@ -491,7 +521,8 @@ func (cn *Conn) getEffectiveWriteTimeout(normalTimeout time.Duration) time.Durat
|
||||
return time.Duration(writeTimeoutNs)
|
||||
}
|
||||
|
||||
nowNs := time.Now().UnixNano()
|
||||
// Use cached time to avoid expensive syscall (max 50ms staleness is acceptable for timeout checks)
|
||||
nowNs := getCachedTimeNs()
|
||||
// Check if deadline has passed
|
||||
if nowNs < deadlineNs {
|
||||
// Deadline is in the future, use relaxed timeout
|
||||
@@ -843,8 +874,10 @@ func (cn *Conn) MaybeHasData() bool {
|
||||
|
||||
// deadline computes the effective deadline time based on context and timeout.
|
||||
// It updates the usedAt timestamp to now.
|
||||
// Uses cached time to avoid expensive syscall (max 50ms staleness is acceptable for deadline calculation).
|
||||
func (cn *Conn) deadline(ctx context.Context, timeout time.Duration) time.Time {
|
||||
tm := time.Now()
|
||||
// Use cached time for deadline calculation (called 2x per command: read + write)
|
||||
tm := time.Unix(0, getCachedTimeNs())
|
||||
cn.SetUsedAt(tm)
|
||||
|
||||
if timeout > 0 {
|
||||
|
||||
@@ -27,6 +27,12 @@ var (
|
||||
// ErrConnUnusableTimeout is returned when a connection is not usable and we timed out trying to mark it as unusable.
|
||||
ErrConnUnusableTimeout = errors.New("redis: timed out trying to mark connection as unusable")
|
||||
|
||||
// errHookRequestedRemoval is returned when a hook requests connection removal.
|
||||
errHookRequestedRemoval = errors.New("hook requested removal")
|
||||
|
||||
// errConnNotPooled is returned when trying to return a non-pooled connection to the pool.
|
||||
errConnNotPooled = errors.New("connection not pooled")
|
||||
|
||||
// popAttempts is the maximum number of attempts to find a usable connection
|
||||
// when popping from the idle connection pool. This handles cases where connections
|
||||
// are temporarily marked as unusable (e.g., during maintenanceNotifications upgrades or network issues).
|
||||
@@ -446,7 +452,8 @@ func (p *ConnPool) getConn(ctx context.Context) (*Conn, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
// Use cached time for health checks (max 50ms staleness is acceptable)
|
||||
now := time.Unix(0, getCachedTimeNs())
|
||||
attempts := 0
|
||||
|
||||
// Lock-free atomic read - no mutex overhead!
|
||||
@@ -661,12 +668,12 @@ func (p *ConnPool) putConn(ctx context.Context, cn *Conn, freeTurn bool) {
|
||||
|
||||
// Combine all removal checks into one - reduces branches
|
||||
if shouldRemove || !shouldPool {
|
||||
p.removeConnInternal(ctx, cn, errors.New("hook requested removal"), freeTurn)
|
||||
p.removeConnInternal(ctx, cn, errHookRequestedRemoval, freeTurn)
|
||||
return
|
||||
}
|
||||
|
||||
if !cn.pooled {
|
||||
p.removeConnInternal(ctx, cn, errors.New("connection not pooled"), freeTurn)
|
||||
p.removeConnInternal(ctx, cn, errConnNotPooled, freeTurn)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user