mirror of
https://github.com/hanzokv/go.git
synced 2026-09-24 00:31:51 +00:00
We do not use Redis. The client was a go-redis fork and still said so
everywhere: `package redis`, `redis.NewClient`, `redis://`, `extra/redisotel`.
Consumers wrote `redis.Client` against a Valkey-backed store called KV.
package redis -> package kv (93 decls)
redis.X -> kv.X (2,794 call sites)
"redis: …" errors -> "kv: …" (206)
extra/redis{otel,cmd,prometheus,census} -> extra/kv{otel,cmd,prometheus,census}
redis:// , rediss:// -> kv:// , kvs:// (ParseURL)
Redis*/*Redis* idents -> KV*/*KV*
REDIS_VERSION, REDIS_ENDPOINTS_CONFIG_PATH -> KV_*
I previously argued this rename was unsafe because much of it was "wire
contract". That was wrong, and the categorisation shows why: RESP command names
are GET/SET/HSET — they never contain the word. Of 5,771 occurrences only two
kinds were server-visible at all: the ParseURL scheme (we choose what we pass;
our configs already use kv://) and the CLIENT SETINFO lib-name label, which the
server merely records. Nothing here changes a byte of protocol.
Two things deliberately keep the old spelling:
RediSearch — names a specific external module the FT.* commands map to.
StoreDist — contains the letters by accident (sto-reDis-t), not the word.
Breaking for consumers by design: every import site must move redis.X -> kv.X.
Minor bump, never a major — this module stays v9 forever.
183 lines
6.2 KiB
Go
183 lines
6.2 KiB
Go
package maintnotifications
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/hanzokv/go/v9/internal"
|
|
"github.com/hanzokv/go/v9/internal/maintnotifications/logs"
|
|
"github.com/hanzokv/go/v9/internal/pool"
|
|
)
|
|
|
|
// OperationsManagerInterface defines the interface for completing handoff operations
|
|
type OperationsManagerInterface interface {
|
|
TrackMovingOperationWithConnID(ctx context.Context, newEndpoint string, deadline time.Time, seqID int64, connID uint64) error
|
|
UntrackOperationWithConnID(seqID int64, connID uint64)
|
|
}
|
|
|
|
// HandoffRequest represents a request to handoff a connection to a new endpoint
|
|
type HandoffRequest struct {
|
|
Conn *pool.Conn
|
|
ConnID uint64 // Unique connection identifier
|
|
Endpoint string
|
|
SeqID int64
|
|
Pool pool.Pooler // Pool to remove connection from on failure
|
|
}
|
|
|
|
// PoolHook implements pool.PoolHook for KV-specific connection handling
|
|
// with maintenance notifications support.
|
|
type PoolHook struct {
|
|
// Base dialer for creating connections to new endpoints during handoffs
|
|
// args are network and address
|
|
baseDialer func(context.Context, string, string) (net.Conn, error)
|
|
|
|
// Network type (e.g., "tcp", "unix")
|
|
network string
|
|
|
|
// Worker manager for background handoff processing
|
|
workerManager *handoffWorkerManager
|
|
|
|
// Configuration for the maintenance notifications
|
|
config *Config
|
|
|
|
// Operations manager interface for operation completion tracking
|
|
operationsManager OperationsManagerInterface
|
|
|
|
// Pool interface for removing connections on handoff failure
|
|
pool pool.Pooler
|
|
}
|
|
|
|
// NewPoolHook creates a new pool hook
|
|
func NewPoolHook(baseDialer func(context.Context, string, string) (net.Conn, error), network string, config *Config, operationsManager OperationsManagerInterface) *PoolHook {
|
|
return NewPoolHookWithPoolSize(baseDialer, network, config, operationsManager, 0)
|
|
}
|
|
|
|
// NewPoolHookWithPoolSize creates a new pool hook with pool size for better worker defaults
|
|
func NewPoolHookWithPoolSize(baseDialer func(context.Context, string, string) (net.Conn, error), network string, config *Config, operationsManager OperationsManagerInterface, poolSize int) *PoolHook {
|
|
// Apply defaults if config is nil or has zero values
|
|
if config == nil {
|
|
config = config.ApplyDefaultsWithPoolSize(poolSize)
|
|
}
|
|
|
|
ph := &PoolHook{
|
|
// baseDialer is used to create connections to new endpoints during handoffs
|
|
baseDialer: baseDialer,
|
|
network: network,
|
|
config: config,
|
|
operationsManager: operationsManager,
|
|
}
|
|
|
|
// Create worker manager
|
|
ph.workerManager = newHandoffWorkerManager(config, ph)
|
|
|
|
return ph
|
|
}
|
|
|
|
// SetPool sets the pool interface for removing connections on handoff failure
|
|
func (ph *PoolHook) SetPool(pooler pool.Pooler) {
|
|
ph.pool = pooler
|
|
}
|
|
|
|
// GetCurrentWorkers returns the current number of active workers (for testing)
|
|
func (ph *PoolHook) GetCurrentWorkers() int {
|
|
return ph.workerManager.getCurrentWorkers()
|
|
}
|
|
|
|
// IsHandoffPending returns true if the given connection has a pending handoff
|
|
func (ph *PoolHook) IsHandoffPending(conn *pool.Conn) bool {
|
|
return ph.workerManager.isHandoffPending(conn)
|
|
}
|
|
|
|
// GetPendingMap returns the pending map for testing purposes
|
|
func (ph *PoolHook) GetPendingMap() *sync.Map {
|
|
return ph.workerManager.getPendingMap()
|
|
}
|
|
|
|
// GetMaxWorkers returns the max workers for testing purposes
|
|
func (ph *PoolHook) GetMaxWorkers() int {
|
|
return ph.workerManager.getMaxWorkers()
|
|
}
|
|
|
|
// GetHandoffQueue returns the handoff queue for testing purposes
|
|
func (ph *PoolHook) GetHandoffQueue() chan HandoffRequest {
|
|
return ph.workerManager.getHandoffQueue()
|
|
}
|
|
|
|
// GetCircuitBreakerStats returns circuit breaker statistics for monitoring
|
|
func (ph *PoolHook) GetCircuitBreakerStats() []CircuitBreakerStats {
|
|
return ph.workerManager.getCircuitBreakerStats()
|
|
}
|
|
|
|
// ResetCircuitBreakers resets all circuit breakers (useful for testing)
|
|
func (ph *PoolHook) ResetCircuitBreakers() {
|
|
ph.workerManager.resetCircuitBreakers()
|
|
}
|
|
|
|
// OnGet is called when a connection is retrieved from the pool
|
|
func (ph *PoolHook) OnGet(_ context.Context, conn *pool.Conn, _ bool) (accept bool, err error) {
|
|
// Check if connection is marked for handoff
|
|
// This prevents using connections that have received MOVING notifications
|
|
if conn.ShouldHandoff() {
|
|
return false, ErrConnectionMarkedForHandoffWithState
|
|
}
|
|
|
|
// Check if connection is usable (not in UNUSABLE or CLOSED state)
|
|
// This ensures we don't return connections that are currently being handed off or re-authenticated.
|
|
if !conn.IsUsable() {
|
|
return false, ErrConnectionMarkedForHandoff
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// OnPut is called when a connection is returned to the pool
|
|
func (ph *PoolHook) OnPut(ctx context.Context, conn *pool.Conn) (shouldPool bool, shouldRemove bool, err error) {
|
|
// first check if we should handoff for faster rejection
|
|
if !conn.ShouldHandoff() {
|
|
// Default behavior (no handoff): pool the connection
|
|
return true, false, nil
|
|
}
|
|
|
|
// check pending handoff to not queue the same connection twice
|
|
if ph.workerManager.isHandoffPending(conn) {
|
|
// Default behavior (pending handoff): pool the connection
|
|
return true, false, nil
|
|
}
|
|
|
|
if err := ph.workerManager.queueHandoff(conn); err != nil {
|
|
// Failed to queue handoff, remove the connection
|
|
internal.Logger.Printf(ctx, logs.FailedToQueueHandoff(conn.GetID(), err))
|
|
// Don't pool, remove connection, no error to caller
|
|
return false, true, nil
|
|
}
|
|
|
|
// Check if handoff was already processed by a worker before we can mark it as queued
|
|
if !conn.ShouldHandoff() {
|
|
// Handoff was already processed - this is normal and the connection should be pooled
|
|
return true, false, nil
|
|
}
|
|
|
|
if err := conn.MarkQueuedForHandoff(); err != nil {
|
|
// If marking fails, check if handoff was processed in the meantime
|
|
if !conn.ShouldHandoff() {
|
|
// Handoff was processed - this is normal, pool the connection
|
|
return true, false, nil
|
|
}
|
|
// Other error - remove the connection
|
|
return false, true, nil
|
|
}
|
|
internal.Logger.Printf(ctx, logs.MarkedForHandoff(conn.GetID()))
|
|
return true, false, nil
|
|
}
|
|
|
|
func (ph *PoolHook) OnRemove(_ context.Context, _ *pool.Conn, _ error) {
|
|
// Not used
|
|
}
|
|
|
|
// Shutdown gracefully shuts down the processor, waiting for workers to complete
|
|
func (ph *PoolHook) Shutdown(ctx context.Context) error {
|
|
return ph.workerManager.shutdownWorkers(ctx)
|
|
}
|