Skip to content

Commit 55c502d

Browse files
committed
try to cache time if for non-critical calculation
1 parent bec09a5 commit 55c502d

File tree

2 files changed

+46
-6
lines changed

2 files changed

+46
-6
lines changed

internal/pool/conn.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,35 @@ import (
1818

1919
var noDeadline = time.Time{}
2020

21+
// Global time cache updated every 50ms by background goroutine.
22+
// This avoids expensive time.Now() syscalls in hot paths like getEffectiveReadTimeout.
23+
// Max staleness: 50ms, which is acceptable for timeout deadline checks (timeouts are typically 3-30 seconds).
24+
var globalTimeCache struct {
25+
nowNs atomic.Int64
26+
}
27+
28+
func init() {
29+
// Initialize immediately
30+
globalTimeCache.nowNs.Store(time.Now().UnixNano())
31+
32+
// Start background updater
33+
go func() {
34+
ticker := time.NewTicker(50 * time.Millisecond)
35+
defer ticker.Stop()
36+
37+
for range ticker.C {
38+
globalTimeCache.nowNs.Store(time.Now().UnixNano())
39+
}
40+
}()
41+
}
42+
43+
// getCachedTimeNs returns the current time in nanoseconds from the global cache.
44+
// This is updated every 50ms by a background goroutine, avoiding expensive syscalls.
45+
// Max staleness: 50ms.
46+
func getCachedTimeNs() int64 {
47+
return globalTimeCache.nowNs.Load()
48+
}
49+
2150
// Global atomic counter for connection IDs
2251
var connIDCounter uint64
2352

@@ -458,7 +487,8 @@ func (cn *Conn) getEffectiveReadTimeout(normalTimeout time.Duration) time.Durati
458487
return time.Duration(readTimeoutNs)
459488
}
460489

461-
nowNs := time.Now().UnixNano()
490+
// Use cached time to avoid expensive syscall (max 50ms staleness is acceptable for timeout checks)
491+
nowNs := getCachedTimeNs()
462492
// Check if deadline has passed
463493
if nowNs < deadlineNs {
464494
// Deadline is in the future, use relaxed timeout
@@ -491,7 +521,8 @@ func (cn *Conn) getEffectiveWriteTimeout(normalTimeout time.Duration) time.Durat
491521
return time.Duration(writeTimeoutNs)
492522
}
493523

494-
nowNs := time.Now().UnixNano()
524+
// Use cached time to avoid expensive syscall (max 50ms staleness is acceptable for timeout checks)
525+
nowNs := getCachedTimeNs()
495526
// Check if deadline has passed
496527
if nowNs < deadlineNs {
497528
// Deadline is in the future, use relaxed timeout
@@ -843,8 +874,10 @@ func (cn *Conn) MaybeHasData() bool {
843874

844875
// deadline computes the effective deadline time based on context and timeout.
845876
// It updates the usedAt timestamp to now.
877+
// Uses cached time to avoid expensive syscall (max 50ms staleness is acceptable for deadline calculation).
846878
func (cn *Conn) deadline(ctx context.Context, timeout time.Duration) time.Time {
847-
tm := time.Now()
879+
// Use cached time for deadline calculation (called 2x per command: read + write)
880+
tm := time.Unix(0, getCachedTimeNs())
848881
cn.SetUsedAt(tm)
849882

850883
if timeout > 0 {

internal/pool/pool.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ var (
2727
// ErrConnUnusableTimeout is returned when a connection is not usable and we timed out trying to mark it as unusable.
2828
ErrConnUnusableTimeout = errors.New("redis: timed out trying to mark connection as unusable")
2929

30+
// errHookRequestedRemoval is returned when a hook requests connection removal.
31+
errHookRequestedRemoval = errors.New("hook requested removal")
32+
33+
// errConnNotPooled is returned when trying to return a non-pooled connection to the pool.
34+
errConnNotPooled = errors.New("connection not pooled")
35+
3036
// popAttempts is the maximum number of attempts to find a usable connection
3137
// when popping from the idle connection pool. This handles cases where connections
3238
// 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) {
446452
return nil, err
447453
}
448454

449-
now := time.Now()
455+
// Use cached time for health checks (max 50ms staleness is acceptable)
456+
now := time.Unix(0, getCachedTimeNs())
450457
attempts := 0
451458

452459
// Lock-free atomic read - no mutex overhead!
@@ -661,12 +668,12 @@ func (p *ConnPool) putConn(ctx context.Context, cn *Conn, freeTurn bool) {
661668

662669
// Combine all removal checks into one - reduces branches
663670
if shouldRemove || !shouldPool {
664-
p.removeConnInternal(ctx, cn, errors.New("hook requested removal"), freeTurn)
671+
p.removeConnInternal(ctx, cn, errHookRequestedRemoval, freeTurn)
665672
return
666673
}
667674

668675
if !cn.pooled {
669-
p.removeConnInternal(ctx, cn, errors.New("connection not pooled"), freeTurn)
676+
p.removeConnInternal(ctx, cn, errConnNotPooled, freeTurn)
670677
return
671678
}
672679

0 commit comments

Comments
 (0)