mirror of
https://github.com/redis/go-redis.git
synced 2025-07-28 06:42:00 +03:00
docs(devdocs): Add generated dev documentation
Add AI generated dev documentation in the `docs` folder.
This commit is contained in:
@ -46,6 +46,7 @@ in the `go.mod` to `go 1.24` in one of the next releases.
|
|||||||
|
|
||||||
- [English](https://redis.uptrace.dev)
|
- [English](https://redis.uptrace.dev)
|
||||||
- [简体中文](https://redis.uptrace.dev/zh/)
|
- [简体中文](https://redis.uptrace.dev/zh/)
|
||||||
|
- [AI-Generated Developer Documentation](./docs) - Detailed technical documentation about the client's implementation, architecture, and internals
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
|
70
docs/README.md
Normal file
70
docs/README.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Redis Client Documentation
|
||||||
|
|
||||||
|
This documentation is AI-generated and provides a comprehensive overview of the Redis client implementation. The documentation is organized into several key files, each focusing on different aspects of the Redis client.
|
||||||
|
|
||||||
|
## Documentation Structure
|
||||||
|
|
||||||
|
### 1. General Architecture ([`general_architecture.md`](general_architecture.md))
|
||||||
|
- High-level architecture of the Redis client
|
||||||
|
- Core components and their interactions
|
||||||
|
- Connection management
|
||||||
|
- Command processing pipeline
|
||||||
|
- Error handling
|
||||||
|
- Monitoring and instrumentation
|
||||||
|
- Best practices and patterns
|
||||||
|
|
||||||
|
### 2. Connection Pool Implementation ([`redis_pool.md`](redis_pool.md))
|
||||||
|
- Detailed explanation of the connection pool system
|
||||||
|
- Pool configuration options
|
||||||
|
- Connection lifecycle management
|
||||||
|
- Pool statistics and monitoring
|
||||||
|
- Error handling in the pool
|
||||||
|
- Performance considerations
|
||||||
|
- Best practices for pool usage
|
||||||
|
|
||||||
|
### 3. Command Processing ([`redis_command_processing.md`](redis_command_processing.md))
|
||||||
|
- Command interface and implementation
|
||||||
|
- Command execution pipeline
|
||||||
|
- Different execution modes (single, pipeline, transaction)
|
||||||
|
- Command types and categories
|
||||||
|
- Error handling and retries
|
||||||
|
- Best practices for command usage
|
||||||
|
- Monitoring and debugging
|
||||||
|
|
||||||
|
### 4. Testing Framework ([`redis_testing.md`](redis_testing.md))
|
||||||
|
- Test environment setup using Docker
|
||||||
|
- Environment variables and configuration
|
||||||
|
- Running tests with Makefile commands
|
||||||
|
- Writing tests with Ginkgo and Gomega
|
||||||
|
- Test organization and patterns
|
||||||
|
- Coverage reporting
|
||||||
|
- Best practices for testing
|
||||||
|
|
||||||
|
### 5. Clients and Connections ([`clients-and-connections.md`](clients-and-connections.md))
|
||||||
|
- Detailed client types and their usage
|
||||||
|
- Connection management and configuration
|
||||||
|
- Client-specific features and optimizations
|
||||||
|
- Connection pooling strategies
|
||||||
|
- Best practices for client usage
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
1. This documentation is AI-generated and should be reviewed for accuracy
|
||||||
|
2. The documentation is based on the actual codebase implementation
|
||||||
|
3. All examples and code snippets are verified against the source code
|
||||||
|
4. The documentation is regularly updated to reflect changes in the codebase
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
For detailed information about contributing to the project, please see the [Contributing Guide](../CONTRIBUTING.md) in the root directory.
|
||||||
|
|
||||||
|
If you find any inaccuracies or would like to suggest improvements to the documentation, please:
|
||||||
|
1. Review the actual code implementation
|
||||||
|
2. Submit a pull request with the proposed changes
|
||||||
|
3. Include references to the relevant code files
|
||||||
|
|
||||||
|
## Related Resources
|
||||||
|
|
||||||
|
- [Go Redis Client GitHub Repository](https://github.com/redis/go-redis)
|
||||||
|
- [Redis Official Documentation](https://redis.io/documentation)
|
||||||
|
- [Go Documentation](https://golang.org/doc/)
|
874
docs/clients-and-connections.md
Normal file
874
docs/clients-and-connections.md
Normal file
@ -0,0 +1,874 @@
|
|||||||
|
# Redis Client Architecture
|
||||||
|
|
||||||
|
This document explains the relationships between different components of the Redis client implementation, focusing on client types, connections, pools, and hooks.
|
||||||
|
|
||||||
|
## Client Hierarchy
|
||||||
|
|
||||||
|
### Component Relationships
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class baseClient {
|
||||||
|
+*Options opt
|
||||||
|
+pool.Pooler connPool
|
||||||
|
+hooksMixin
|
||||||
|
+onClose func() error
|
||||||
|
+clone() *baseClient
|
||||||
|
+initConn()
|
||||||
|
+process()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Client {
|
||||||
|
+baseClient
|
||||||
|
+cmdable
|
||||||
|
+hooksMixin
|
||||||
|
+NewClient()
|
||||||
|
+WithTimeout()
|
||||||
|
+Pipeline()
|
||||||
|
+TxPipeline()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Conn {
|
||||||
|
+baseClient
|
||||||
|
+cmdable
|
||||||
|
+statefulCmdable
|
||||||
|
+hooksMixin
|
||||||
|
+newConn()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pipeline {
|
||||||
|
+exec pipelineExecer
|
||||||
|
+init()
|
||||||
|
+Pipelined()
|
||||||
|
}
|
||||||
|
|
||||||
|
class TxPipeline {
|
||||||
|
+Pipeline
|
||||||
|
+wrapMultiExec()
|
||||||
|
}
|
||||||
|
|
||||||
|
class hooksMixin {
|
||||||
|
+*sync.RWMutex hooksMu
|
||||||
|
+[]Hook slice
|
||||||
|
+hooks initial
|
||||||
|
+hooks current
|
||||||
|
+clone() hooksMixin
|
||||||
|
+AddHook()
|
||||||
|
+chain()
|
||||||
|
}
|
||||||
|
|
||||||
|
Client --> baseClient : embeds
|
||||||
|
Conn --> baseClient : embeds
|
||||||
|
Pipeline --> Client : uses
|
||||||
|
TxPipeline --> Pipeline : extends
|
||||||
|
baseClient --> hooksMixin : contains
|
||||||
|
Client --> hooksMixin : contains
|
||||||
|
Conn --> hooksMixin : contains
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook Chain Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Client
|
||||||
|
participant Hook1
|
||||||
|
participant Hook2
|
||||||
|
participant Redis
|
||||||
|
|
||||||
|
Client->>Hook1: Execute Command
|
||||||
|
Hook1->>Hook2: Next Hook
|
||||||
|
Hook2->>Redis: Execute Command
|
||||||
|
Redis-->>Hook2: Response
|
||||||
|
Hook2-->>Hook1: Process Response
|
||||||
|
Hook1-->>Client: Final Response
|
||||||
|
```
|
||||||
|
|
||||||
|
### Connection Pool Management
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Idle
|
||||||
|
Idle --> InUse: Get Connection
|
||||||
|
InUse --> Idle: Release Connection
|
||||||
|
InUse --> Closed: Connection Error
|
||||||
|
Idle --> Closed: Pool Shutdown
|
||||||
|
Closed --> [*]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pipeline Execution Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Client
|
||||||
|
participant Pipeline
|
||||||
|
participant Redis
|
||||||
|
|
||||||
|
Client->>Pipeline: Queue Command 1
|
||||||
|
Client->>Pipeline: Queue Command 2
|
||||||
|
Client->>Pipeline: Queue Command 3
|
||||||
|
Pipeline->>Redis: Send Batch
|
||||||
|
Redis-->>Pipeline: Batch Response
|
||||||
|
Pipeline-->>Client: Processed Responses
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transaction Pipeline Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Client
|
||||||
|
participant TxPipeline
|
||||||
|
participant Redis
|
||||||
|
|
||||||
|
Client->>TxPipeline: Queue Command 1
|
||||||
|
Client->>TxPipeline: Queue Command 2
|
||||||
|
TxPipeline->>Redis: MULTI
|
||||||
|
TxPipeline->>Redis: Command 1
|
||||||
|
TxPipeline->>Redis: Command 2
|
||||||
|
TxPipeline->>Redis: EXEC
|
||||||
|
Redis-->>TxPipeline: Transaction Result
|
||||||
|
TxPipeline-->>Client: Processed Results
|
||||||
|
```
|
||||||
|
|
||||||
|
### Base Client (`baseClient`)
|
||||||
|
|
||||||
|
The `baseClient` is the foundation of all Redis client implementations. It contains:
|
||||||
|
- Connection pool management
|
||||||
|
- Basic Redis command execution
|
||||||
|
- Hook management
|
||||||
|
- Connection lifecycle handling
|
||||||
|
|
||||||
|
```go
|
||||||
|
type baseClient struct {
|
||||||
|
opt *Options
|
||||||
|
connPool pool.Pooler
|
||||||
|
hooksMixin
|
||||||
|
onClose func() error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client Types
|
||||||
|
|
||||||
|
1. **Client (`Client`)**
|
||||||
|
- The main Redis client used by applications
|
||||||
|
- Represents a pool of connections
|
||||||
|
- Safe for concurrent use
|
||||||
|
- Embeds `baseClient` and adds command execution capabilities
|
||||||
|
- Primary entry point for most Redis operations
|
||||||
|
- Handles connection pooling and retries automatically
|
||||||
|
|
||||||
|
2. **Conn (`Conn`)**
|
||||||
|
- Represents a single Redis connection
|
||||||
|
- Used for stateful operations like pub/sub
|
||||||
|
- Required for blocking operations (BLPOP, BRPOP)
|
||||||
|
- Also embeds `baseClient`
|
||||||
|
- Has additional stateful command capabilities
|
||||||
|
- Not safe for concurrent use
|
||||||
|
|
||||||
|
3. **Pipeline (`Pipeline`)**
|
||||||
|
- Used for pipelining multiple commands
|
||||||
|
- Not a standalone client, but a wrapper around existing clients
|
||||||
|
- Batches commands and sends them in a single network roundtrip
|
||||||
|
|
||||||
|
4. **Transaction Pipeline (`TxPipeline`)**
|
||||||
|
- Similar to Pipeline but wraps commands in MULTI/EXEC
|
||||||
|
- Ensures atomic execution of commands
|
||||||
|
- Also a wrapper around existing clients
|
||||||
|
|
||||||
|
## Pointer vs Value Semantics
|
||||||
|
|
||||||
|
### When `baseClient` is a Pointer
|
||||||
|
|
||||||
|
The `baseClient` is used as a pointer in these scenarios:
|
||||||
|
|
||||||
|
1. **Client Creation**
|
||||||
|
```go
|
||||||
|
func NewClient(opt *Options) *Client {
|
||||||
|
c := Client{
|
||||||
|
baseClient: &baseClient{
|
||||||
|
opt: opt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Used as pointer to share the same base client instance
|
||||||
|
- Allows modifications to propagate to all references
|
||||||
|
- More efficient for large structs
|
||||||
|
|
||||||
|
2. **Connection Pooling**
|
||||||
|
- Pooled connections need to share the same base client configuration
|
||||||
|
- Pointer semantics ensure consistent behavior across pooled connections
|
||||||
|
|
||||||
|
### When `baseClient` is a Value
|
||||||
|
|
||||||
|
The `baseClient` is used as a value in these scenarios:
|
||||||
|
|
||||||
|
1. **Cloning**
|
||||||
|
```go
|
||||||
|
func (c *baseClient) clone() *baseClient {
|
||||||
|
clone := *c
|
||||||
|
clone.hooksMixin = c.hooksMixin.clone()
|
||||||
|
return &clone
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Creates independent copies for isolation
|
||||||
|
- Prevents unintended sharing of state
|
||||||
|
- Used when creating new connections or client instances
|
||||||
|
|
||||||
|
2. **Temporary Operations**
|
||||||
|
- When creating short-lived client instances
|
||||||
|
- When isolation is required for specific operations
|
||||||
|
|
||||||
|
## Hooks Management
|
||||||
|
|
||||||
|
### HooksMixin
|
||||||
|
|
||||||
|
The `hooksMixin` is a struct that manages hook chains for different operations:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type hooksMixin struct {
|
||||||
|
hooksMu *sync.RWMutex
|
||||||
|
slice []Hook
|
||||||
|
initial hooks
|
||||||
|
current hooks
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook Types
|
||||||
|
|
||||||
|
1. **Dial Hook**
|
||||||
|
- Called during connection establishment
|
||||||
|
- Can modify connection parameters
|
||||||
|
- Used for custom connection handling
|
||||||
|
|
||||||
|
2. **Process Hook**
|
||||||
|
- Called before command execution
|
||||||
|
- Can modify commands or add logging
|
||||||
|
- Used for command monitoring
|
||||||
|
|
||||||
|
3. **Pipeline Hook**
|
||||||
|
- Called during pipeline execution
|
||||||
|
- Handles batch command processing
|
||||||
|
- Used for pipeline monitoring
|
||||||
|
|
||||||
|
### Hook Lifecycle
|
||||||
|
|
||||||
|
1. **Initialization**
|
||||||
|
- Hooks are initialized when creating a new client
|
||||||
|
- Default hooks are set up for basic operations
|
||||||
|
- Hooks can be added or removed at runtime
|
||||||
|
|
||||||
|
2. **Hook Chain**
|
||||||
|
- Hooks are chained in LIFO (Last In, First Out) order
|
||||||
|
- Each hook can modify the command or response
|
||||||
|
- Chain can be modified at runtime
|
||||||
|
- Hooks can prevent command execution by not calling next
|
||||||
|
|
||||||
|
3. **Hook Inheritance**
|
||||||
|
- New connections inherit hooks from their parent client
|
||||||
|
- Hooks are cloned to prevent shared state
|
||||||
|
- Each connection maintains its own hook chain
|
||||||
|
- Hook modifications in child don't affect parent
|
||||||
|
|
||||||
|
## Connection Pooling
|
||||||
|
|
||||||
|
### Pool Types
|
||||||
|
|
||||||
|
1. **Single Connection Pool**
|
||||||
|
- Used for dedicated connections
|
||||||
|
- No connection sharing
|
||||||
|
- Used in `Conn` type
|
||||||
|
|
||||||
|
2. **Multi Connection Pool**
|
||||||
|
- Used for client pools
|
||||||
|
- Manages multiple connections
|
||||||
|
- Handles connection reuse
|
||||||
|
|
||||||
|
### Pool Management
|
||||||
|
|
||||||
|
1. **Connection Acquisition**
|
||||||
|
- Connections are acquired from the pool
|
||||||
|
- Pool maintains minimum and maximum connections
|
||||||
|
- Handles connection timeouts
|
||||||
|
|
||||||
|
2. **Connection Release**
|
||||||
|
- Connections are returned to the pool
|
||||||
|
- Pool handles connection cleanup
|
||||||
|
- Manages connection lifecycle
|
||||||
|
|
||||||
|
### Pool Configuration
|
||||||
|
|
||||||
|
1. **Pool Options**
|
||||||
|
- Minimum idle connections
|
||||||
|
- Maximum active connections
|
||||||
|
- Connection idle timeout
|
||||||
|
- Connection lifetime
|
||||||
|
- Pool health check interval
|
||||||
|
|
||||||
|
2. **Health Checks**
|
||||||
|
- Periodic connection validation
|
||||||
|
- Automatic reconnection on failure
|
||||||
|
- Connection cleanup on errors
|
||||||
|
- Pool size maintenance
|
||||||
|
|
||||||
|
## Transaction and Pipeline Handling
|
||||||
|
|
||||||
|
### Pipeline
|
||||||
|
|
||||||
|
1. **Command Batching**
|
||||||
|
- Commands are queued in memory
|
||||||
|
- Sent in a single network roundtrip
|
||||||
|
- Responses are collected in order
|
||||||
|
|
||||||
|
2. **Error Handling**
|
||||||
|
- Pipeline execution is atomic
|
||||||
|
- Errors are propagated to all commands
|
||||||
|
- Connection errors trigger retries
|
||||||
|
|
||||||
|
### Transaction Pipeline
|
||||||
|
|
||||||
|
1. **MULTI/EXEC Wrapping**
|
||||||
|
- Commands are wrapped in MULTI/EXEC
|
||||||
|
- Ensures atomic execution
|
||||||
|
- Handles transaction errors
|
||||||
|
|
||||||
|
2. **State Management**
|
||||||
|
- Maintains transaction state
|
||||||
|
- Handles rollback scenarios
|
||||||
|
- Manages connection state
|
||||||
|
|
||||||
|
### Pipeline Limitations
|
||||||
|
|
||||||
|
1. **Size Limits**
|
||||||
|
- Maximum commands per pipeline
|
||||||
|
- Memory usage considerations
|
||||||
|
- Network buffer size limits
|
||||||
|
- Response size handling
|
||||||
|
|
||||||
|
2. **Transaction Behavior**
|
||||||
|
- WATCH/UNWATCH key monitoring
|
||||||
|
- Transaction isolation
|
||||||
|
- Rollback on failure
|
||||||
|
- Atomic execution guarantees
|
||||||
|
|
||||||
|
## Error Handling and Cleanup
|
||||||
|
|
||||||
|
### Error Types and Handling
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class Error {
|
||||||
|
<<interface>>
|
||||||
|
+Error() string
|
||||||
|
}
|
||||||
|
|
||||||
|
class RedisError {
|
||||||
|
+string message
|
||||||
|
+Error() string
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectionError {
|
||||||
|
+string message
|
||||||
|
+net.Error
|
||||||
|
+Temporary() bool
|
||||||
|
+Timeout() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
class TxFailedError {
|
||||||
|
+string message
|
||||||
|
+Error() string
|
||||||
|
}
|
||||||
|
|
||||||
|
Error <|-- RedisError
|
||||||
|
Error <|-- ConnectionError
|
||||||
|
Error <|-- TxFailedError
|
||||||
|
net.Error <|-- ConnectionError
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Client
|
||||||
|
participant Connection
|
||||||
|
participant Redis
|
||||||
|
participant ErrorHandler
|
||||||
|
|
||||||
|
Client->>Connection: Execute Command
|
||||||
|
Connection->>Redis: Send Command
|
||||||
|
alt Connection Error
|
||||||
|
Redis-->>Connection: Connection Error
|
||||||
|
Connection->>ErrorHandler: Handle Connection Error
|
||||||
|
ErrorHandler->>Connection: Retry or Close
|
||||||
|
else Redis Error
|
||||||
|
Redis-->>Connection: Redis Error
|
||||||
|
Connection->>Client: Return Error
|
||||||
|
else Transaction Error
|
||||||
|
Redis-->>Connection: Transaction Failed
|
||||||
|
Connection->>Client: Return TxFailedError
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Connection and Client Cleanup
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant User
|
||||||
|
participant Client
|
||||||
|
participant ConnectionPool
|
||||||
|
participant Connection
|
||||||
|
participant Hook
|
||||||
|
|
||||||
|
User->>Client: Close()
|
||||||
|
Client->>Hook: Execute Close Hooks
|
||||||
|
Hook-->>Client: Hook Complete
|
||||||
|
Client->>ConnectionPool: Close All Connections
|
||||||
|
ConnectionPool->>Connection: Close
|
||||||
|
Connection->>Redis: Close Connection
|
||||||
|
Redis-->>Connection: Connection Closed
|
||||||
|
Connection-->>ConnectionPool: Connection Removed
|
||||||
|
ConnectionPool-->>Client: Pool Closed
|
||||||
|
Client-->>User: Client Closed
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling Strategies
|
||||||
|
|
||||||
|
1. **Connection Errors**
|
||||||
|
- Temporary errors (network issues) trigger retries
|
||||||
|
- Permanent errors (invalid credentials) close the connection
|
||||||
|
- Connection pool handles reconnection attempts
|
||||||
|
- Maximum retry attempts configurable via options
|
||||||
|
|
||||||
|
2. **Redis Errors**
|
||||||
|
- Command-specific errors returned to caller
|
||||||
|
- No automatic retries for Redis errors
|
||||||
|
- Error types include:
|
||||||
|
- Command syntax errors
|
||||||
|
- Type errors
|
||||||
|
- Permission errors
|
||||||
|
- Resource limit errors
|
||||||
|
|
||||||
|
3. **Transaction Errors**
|
||||||
|
- MULTI/EXEC failures return `TxFailedError`
|
||||||
|
- Individual command errors within transaction
|
||||||
|
- Watch/Unwatch failures
|
||||||
|
- Connection errors during transaction
|
||||||
|
|
||||||
|
### Cleanup Process
|
||||||
|
|
||||||
|
1. **Client Cleanup**
|
||||||
|
```go
|
||||||
|
func (c *Client) Close() error {
|
||||||
|
// Execute close hooks
|
||||||
|
if c.onClose != nil {
|
||||||
|
c.onClose()
|
||||||
|
}
|
||||||
|
// Close connection pool
|
||||||
|
return c.connPool.Close()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Executes registered close hooks
|
||||||
|
- Closes all connections in pool
|
||||||
|
- Releases all resources
|
||||||
|
- Thread-safe operation
|
||||||
|
|
||||||
|
2. **Connection Cleanup**
|
||||||
|
```go
|
||||||
|
func (c *Conn) Close() error {
|
||||||
|
// Cleanup connection state
|
||||||
|
c.state = closed
|
||||||
|
// Close underlying connection
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Closes underlying network connection
|
||||||
|
- Cleans up connection state
|
||||||
|
- Removes from connection pool
|
||||||
|
- Handles pending operations
|
||||||
|
|
||||||
|
3. **Pool Cleanup**
|
||||||
|
- Closes all idle connections
|
||||||
|
- Waits for in-use connections
|
||||||
|
- Handles connection timeouts
|
||||||
|
- Releases pool resources
|
||||||
|
|
||||||
|
### Best Practices for Error Handling
|
||||||
|
|
||||||
|
1. **Connection Management**
|
||||||
|
- Always check for connection errors
|
||||||
|
- Implement proper retry logic
|
||||||
|
- Handle connection timeouts
|
||||||
|
- Monitor connection pool health
|
||||||
|
|
||||||
|
2. **Resource Cleanup**
|
||||||
|
- Always call Close() when done
|
||||||
|
- Use defer for cleanup in critical sections
|
||||||
|
- Handle cleanup errors
|
||||||
|
- Monitor resource usage
|
||||||
|
|
||||||
|
3. **Error Recovery**
|
||||||
|
- Implement circuit breakers
|
||||||
|
- Use backoff strategies
|
||||||
|
- Monitor error patterns
|
||||||
|
- Log error details
|
||||||
|
|
||||||
|
4. **Transaction Safety**
|
||||||
|
- Check transaction results
|
||||||
|
- Handle watch/unwatch failures
|
||||||
|
- Implement rollback strategies
|
||||||
|
- Monitor transaction timeouts
|
||||||
|
|
||||||
|
### Context and Cancellation
|
||||||
|
|
||||||
|
1. **Context Usage**
|
||||||
|
- Command execution timeout
|
||||||
|
- Connection establishment timeout
|
||||||
|
- Operation cancellation
|
||||||
|
- Resource cleanup on cancellation
|
||||||
|
|
||||||
|
2. **Pool Error Handling**
|
||||||
|
- Connection acquisition timeout
|
||||||
|
- Pool exhaustion handling
|
||||||
|
- Connection validation errors
|
||||||
|
- Resource cleanup on errors
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Client Usage**
|
||||||
|
- Use `Client` for most operations
|
||||||
|
- Use `Conn` for stateful operations
|
||||||
|
- Use pipelines for batch operations
|
||||||
|
|
||||||
|
2. **Hook Implementation**
|
||||||
|
- Keep hooks lightweight
|
||||||
|
- Handle errors properly
|
||||||
|
- Call next hook in chain
|
||||||
|
|
||||||
|
3. **Connection Management**
|
||||||
|
- Let the pool handle connections
|
||||||
|
- Don't manually manage connections
|
||||||
|
- Use appropriate timeouts
|
||||||
|
|
||||||
|
4. **Error Handling**
|
||||||
|
- Check command errors
|
||||||
|
- Handle connection errors
|
||||||
|
- Implement retry logic when needed
|
||||||
|
|
||||||
|
## Deep Dive: baseClient Embedding Strategies
|
||||||
|
|
||||||
|
### Implementation Examples
|
||||||
|
|
||||||
|
1. **Client Implementation (Pointer Embedding)**
|
||||||
|
```go
|
||||||
|
type Client struct {
|
||||||
|
*baseClient // Pointer embedding
|
||||||
|
cmdable
|
||||||
|
hooksMixin
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(opt *Options) *Client {
|
||||||
|
c := Client{
|
||||||
|
baseClient: &baseClient{ // Created as pointer
|
||||||
|
opt: opt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.init()
|
||||||
|
c.connPool = newConnPool(opt, c.dialHook)
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The `Client` uses pointer embedding because:
|
||||||
|
- It needs to share the same `baseClient` instance across all operations
|
||||||
|
- The `baseClient` contains connection pool and options that should be shared
|
||||||
|
- Modifications to the base client (like timeouts) should affect all operations
|
||||||
|
- More efficient for large structs since it avoids copying
|
||||||
|
|
||||||
|
2. **Conn Implementation (Value Embedding)**
|
||||||
|
```go
|
||||||
|
type Conn struct {
|
||||||
|
baseClient // Value embedding
|
||||||
|
cmdable
|
||||||
|
statefulCmdable
|
||||||
|
hooksMixin
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConn(opt *Options, connPool pool.Pooler, parentHooks hooksMixin) *Conn {
|
||||||
|
c := Conn{
|
||||||
|
baseClient: baseClient{ // Created as value
|
||||||
|
opt: opt,
|
||||||
|
connPool: connPool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.cmdable = c.Process
|
||||||
|
c.statefulCmdable = c.Process
|
||||||
|
c.hooksMixin = parentHooks.clone()
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The `Conn` uses value embedding because:
|
||||||
|
- Each connection needs its own independent state
|
||||||
|
- Connections are short-lived and don't need to share state
|
||||||
|
- Prevents unintended sharing of connection state
|
||||||
|
- More memory efficient for single connections
|
||||||
|
|
||||||
|
3. **Tx (Transaction) Implementation (Value Embedding)**
|
||||||
|
```go
|
||||||
|
type Tx struct {
|
||||||
|
baseClient // Value embedding
|
||||||
|
cmdable
|
||||||
|
statefulCmdable
|
||||||
|
hooksMixin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) newTx() *Tx {
|
||||||
|
tx := Tx{
|
||||||
|
baseClient: baseClient{ // Created as value
|
||||||
|
opt: c.opt,
|
||||||
|
connPool: pool.NewStickyConnPool(c.connPool),
|
||||||
|
},
|
||||||
|
hooksMixin: c.hooksMixin.clone(),
|
||||||
|
}
|
||||||
|
tx.init()
|
||||||
|
return &tx
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The `Tx` uses value embedding because:
|
||||||
|
- Transactions need isolated state
|
||||||
|
- Each transaction has its own connection pool
|
||||||
|
- Prevents transaction state from affecting other operations
|
||||||
|
- Ensures atomic execution of commands
|
||||||
|
|
||||||
|
4. **Pipeline Implementation (No baseClient)**
|
||||||
|
```go
|
||||||
|
type Pipeline struct {
|
||||||
|
cmdable
|
||||||
|
statefulCmdable
|
||||||
|
exec pipelineExecer
|
||||||
|
cmds []Cmder
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The `Pipeline` doesn't embed `baseClient` because:
|
||||||
|
- It's a temporary command buffer
|
||||||
|
- Doesn't need its own connection management
|
||||||
|
- Uses the parent client's connection pool
|
||||||
|
- More lightweight without base client overhead
|
||||||
|
|
||||||
|
### Embedding Strategy Comparison
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class Client {
|
||||||
|
+*baseClient
|
||||||
|
+cmdable
|
||||||
|
+hooksMixin
|
||||||
|
+NewClient()
|
||||||
|
+WithTimeout()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Conn {
|
||||||
|
+baseClient
|
||||||
|
+cmdable
|
||||||
|
+statefulCmdable
|
||||||
|
+hooksMixin
|
||||||
|
+newConn()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Tx {
|
||||||
|
+baseClient
|
||||||
|
+cmdable
|
||||||
|
+statefulCmdable
|
||||||
|
+hooksMixin
|
||||||
|
+newTx()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pipeline {
|
||||||
|
+cmdable
|
||||||
|
+statefulCmdable
|
||||||
|
+exec pipelineExecer
|
||||||
|
+cmds []Cmder
|
||||||
|
}
|
||||||
|
|
||||||
|
Client --> baseClient : pointer
|
||||||
|
Conn --> baseClient : value
|
||||||
|
Tx --> baseClient : value
|
||||||
|
Pipeline --> baseClient : none
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Differences in Embedding Strategy
|
||||||
|
|
||||||
|
1. **Pointer Embedding (Client)**
|
||||||
|
- Used when state needs to be shared
|
||||||
|
- More efficient for large structs
|
||||||
|
- Allows modifications to propagate
|
||||||
|
- Better for long-lived instances
|
||||||
|
- Memory Layout:
|
||||||
|
```
|
||||||
|
+-------------------+
|
||||||
|
| Client |
|
||||||
|
| +-------------+ |
|
||||||
|
| | *baseClient | |
|
||||||
|
| +-------------+ |
|
||||||
|
| | cmdable | |
|
||||||
|
| +-------------+ |
|
||||||
|
| | hooksMixin | |
|
||||||
|
| +-------------+ |
|
||||||
|
+-------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Value Embedding (Conn, Tx)**
|
||||||
|
- Used when isolation is needed
|
||||||
|
- Prevents unintended state sharing
|
||||||
|
- Better for short-lived instances
|
||||||
|
- More memory efficient for small instances
|
||||||
|
- Memory Layout:
|
||||||
|
```
|
||||||
|
+-------------------+
|
||||||
|
| Conn/Tx |
|
||||||
|
| +-------------+ |
|
||||||
|
| | baseClient | |
|
||||||
|
| | +---------+ | |
|
||||||
|
| | | Options | | |
|
||||||
|
| | +---------+ | |
|
||||||
|
| | | Pooler | | |
|
||||||
|
| | +---------+ | |
|
||||||
|
| +-------------+ |
|
||||||
|
| | cmdable | |
|
||||||
|
| +-------------+ |
|
||||||
|
| | hooksMixin | |
|
||||||
|
| +-------------+ |
|
||||||
|
+-------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **No Embedding (Pipeline)**
|
||||||
|
- Used for temporary operations
|
||||||
|
- Minimizes memory overhead
|
||||||
|
- Relies on parent client
|
||||||
|
- Better for command batching
|
||||||
|
- Memory Layout:
|
||||||
|
```
|
||||||
|
+-------------------+
|
||||||
|
| Pipeline |
|
||||||
|
| +-------------+ |
|
||||||
|
| | cmdable | |
|
||||||
|
| +-------------+ |
|
||||||
|
| | exec | |
|
||||||
|
| +-------------+ |
|
||||||
|
| | cmds | |
|
||||||
|
| +-------------+ |
|
||||||
|
+-------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
### Design Implications
|
||||||
|
|
||||||
|
1. **Resource Management**
|
||||||
|
- Pointer embedding enables shared resource management
|
||||||
|
- Value embedding ensures resource isolation
|
||||||
|
- No embedding minimizes resource overhead
|
||||||
|
|
||||||
|
2. **State Management**
|
||||||
|
- Pointer embedding allows state propagation
|
||||||
|
- Value embedding prevents state leakage
|
||||||
|
- No embedding avoids state management
|
||||||
|
|
||||||
|
3. **Performance Considerations**
|
||||||
|
- Pointer embedding reduces memory usage for large structs
|
||||||
|
- Value embedding improves locality for small structs
|
||||||
|
- No embedding minimizes memory footprint
|
||||||
|
|
||||||
|
4. **Error Handling**
|
||||||
|
- Pointer embedding centralizes error handling
|
||||||
|
- Value embedding isolates error effects
|
||||||
|
- No embedding delegates error handling
|
||||||
|
|
||||||
|
5. **Cleanup Process**
|
||||||
|
- Pointer embedding requires coordinated cleanup
|
||||||
|
- Value embedding enables independent cleanup
|
||||||
|
- No embedding avoids cleanup complexity
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
1. **When to Use Pointer Embedding**
|
||||||
|
- Long-lived instances
|
||||||
|
- Shared state requirements
|
||||||
|
- Large structs
|
||||||
|
- Centralized management
|
||||||
|
|
||||||
|
2. **When to Use Value Embedding**
|
||||||
|
- Short-lived instances
|
||||||
|
- State isolation needs
|
||||||
|
- Small structs
|
||||||
|
- Independent management
|
||||||
|
|
||||||
|
3. **When to Avoid Embedding**
|
||||||
|
- Temporary operations
|
||||||
|
- Minimal state needs
|
||||||
|
- Command batching
|
||||||
|
- Performance critical paths
|
||||||
|
|
||||||
|
### Authentication System
|
||||||
|
|
||||||
|
#### Streaming Credentials Provider
|
||||||
|
The Redis client supports a streaming credentials provider system that allows for dynamic credential updates:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type StreamingCredentialsProvider interface {
|
||||||
|
Subscribe(listener CredentialsListener) (Credentials, UnsubscribeFunc, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CredentialsListener interface {
|
||||||
|
OnNext(credentials Credentials)
|
||||||
|
OnError(err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Credentials interface {
|
||||||
|
BasicAuth() (username string, password string)
|
||||||
|
RawCredentials() string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Key Features:
|
||||||
|
- Dynamic credential updates
|
||||||
|
- Error handling and propagation
|
||||||
|
- Basic authentication support
|
||||||
|
- Raw credential access
|
||||||
|
- Subscription management
|
||||||
|
|
||||||
|
#### Re-Authentication Listener
|
||||||
|
The client includes a re-authentication listener for handling credential updates:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type ReAuthCredentialsListener struct {
|
||||||
|
reAuth func(credentials Credentials) error
|
||||||
|
onErr func(err error)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Automatic re-authentication on credential updates
|
||||||
|
- Error handling and propagation
|
||||||
|
- Customizable re-authentication logic
|
||||||
|
- Thread-safe operation
|
||||||
|
|
||||||
|
#### Basic Authentication
|
||||||
|
The client provides a basic authentication implementation:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type basicAuth struct {
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBasicCredentials(username, password string) Credentials {
|
||||||
|
return &basicAuth{
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
- Simple username/password authentication
|
||||||
|
- Raw credential string generation
|
||||||
|
- Basic authentication support
|
||||||
|
- Thread-safe operation
|
||||||
|
|
||||||
|
// ... rest of existing content ...
|
809
docs/general_architecture.md
Normal file
809
docs/general_architecture.md
Normal file
@ -0,0 +1,809 @@
|
|||||||
|
# Redis Client Architecture
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document provides a comprehensive description of the Redis client implementation architecture, focusing on the relationships between different components, their responsibilities, and implementation details.
|
||||||
|
|
||||||
|
## Core Components
|
||||||
|
|
||||||
|
### 1. Connection Management
|
||||||
|
|
||||||
|
#### Conn Struct
|
||||||
|
The `Conn` struct represents a single Redis connection and is defined in `internal/pool/conn.go`. It contains:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Conn struct {
|
||||||
|
usedAt int64 // atomic
|
||||||
|
netConn net.Conn
|
||||||
|
rd *proto.Reader
|
||||||
|
bw *bufio.Writer
|
||||||
|
wr *proto.Writer
|
||||||
|
Inited bool
|
||||||
|
pooled bool
|
||||||
|
createdAt time.Time
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Detailed Field Descriptions
|
||||||
|
- `usedAt` (int64, atomic)
|
||||||
|
- Tracks the last usage timestamp of the connection
|
||||||
|
- Uses atomic operations for thread-safe access
|
||||||
|
- Helps in connection health checks and idle timeout management
|
||||||
|
- Updated via `SetUsedAt()` and retrieved via `UsedAt()`
|
||||||
|
|
||||||
|
- `netConn` (net.Conn)
|
||||||
|
- The underlying network connection
|
||||||
|
- Handles raw TCP communication
|
||||||
|
- Supports both TCP and Unix domain socket connections
|
||||||
|
- Can be updated via `SetNetConn()` which also resets the reader and writer
|
||||||
|
|
||||||
|
- `rd` (*proto.Reader)
|
||||||
|
- Redis protocol reader
|
||||||
|
- Handles RESP (REdis Serialization Protocol) parsing
|
||||||
|
- Manages read buffers and protocol state
|
||||||
|
- Created with `proto.NewReader()`
|
||||||
|
|
||||||
|
- `bw` (*bufio.Writer)
|
||||||
|
- Buffered writer for efficient I/O
|
||||||
|
- Reduces system calls by batching writes
|
||||||
|
- Configurable buffer size based on workload
|
||||||
|
- Created with `bufio.NewWriter()`
|
||||||
|
|
||||||
|
- `wr` (*proto.Writer)
|
||||||
|
- Redis protocol writer
|
||||||
|
- Handles RESP serialization
|
||||||
|
- Manages write buffers and protocol state
|
||||||
|
- Created with `proto.NewWriter()`
|
||||||
|
|
||||||
|
- `Inited` (bool)
|
||||||
|
- Indicates if the connection has been initialized
|
||||||
|
- Set after successful authentication and protocol negotiation
|
||||||
|
- Prevents re-initialization of established connections
|
||||||
|
- Used in connection pool management
|
||||||
|
|
||||||
|
- `pooled` (bool)
|
||||||
|
- Indicates if the connection is part of a connection pool
|
||||||
|
- Affects connection lifecycle management
|
||||||
|
- Determines if connection should be returned to pool
|
||||||
|
- Set during connection creation
|
||||||
|
|
||||||
|
- `createdAt` (time.Time)
|
||||||
|
- Records connection creation time
|
||||||
|
- Used for connection lifetime management
|
||||||
|
- Helps in detecting stale connections
|
||||||
|
- Used in `isHealthyConn()` checks
|
||||||
|
|
||||||
|
#### Connection Lifecycle Methods
|
||||||
|
|
||||||
|
##### Creation and Initialization
|
||||||
|
```go
|
||||||
|
func NewConn(netConn net.Conn) *Conn {
|
||||||
|
cn := &Conn{
|
||||||
|
netConn: netConn,
|
||||||
|
createdAt: time.Now(),
|
||||||
|
}
|
||||||
|
cn.rd = proto.NewReader(netConn)
|
||||||
|
cn.bw = bufio.NewWriter(netConn)
|
||||||
|
cn.wr = proto.NewWriter(cn.bw)
|
||||||
|
cn.SetUsedAt(time.Now())
|
||||||
|
return cn
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Usage Tracking
|
||||||
|
```go
|
||||||
|
func (cn *Conn) UsedAt() time.Time {
|
||||||
|
unix := atomic.LoadInt64(&cn.usedAt)
|
||||||
|
return time.Unix(unix, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) SetUsedAt(tm time.Time) {
|
||||||
|
atomic.StoreInt64(&cn.usedAt, tm.Unix())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Network Operations
|
||||||
|
```go
|
||||||
|
func (cn *Conn) WithReader(
|
||||||
|
ctx context.Context, timeout time.Duration, fn func(rd *proto.Reader) error,
|
||||||
|
) error {
|
||||||
|
if timeout >= 0 {
|
||||||
|
if err := cn.netConn.SetReadDeadline(cn.deadline(ctx, timeout)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fn(cn.rd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) WithWriter(
|
||||||
|
ctx context.Context, timeout time.Duration, fn func(wr *proto.Writer) error,
|
||||||
|
) error {
|
||||||
|
if timeout >= 0 {
|
||||||
|
if err := cn.netConn.SetWriteDeadline(cn.deadline(ctx, timeout)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cn.bw.Buffered() > 0 {
|
||||||
|
cn.bw.Reset(cn.netConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fn(cn.wr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cn.bw.Flush()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Connection Pools
|
||||||
|
|
||||||
|
##### 1. ConnPool (`internal/pool/pool.go`)
|
||||||
|
Detailed implementation:
|
||||||
|
```go
|
||||||
|
type ConnPool struct {
|
||||||
|
cfg *Options
|
||||||
|
|
||||||
|
dialErrorsNum uint32 // atomic
|
||||||
|
lastDialError atomic.Value
|
||||||
|
|
||||||
|
queue chan struct{}
|
||||||
|
|
||||||
|
connsMu sync.Mutex
|
||||||
|
conns []*Conn
|
||||||
|
idleConns []*Conn
|
||||||
|
|
||||||
|
poolSize int
|
||||||
|
idleConnsLen int
|
||||||
|
|
||||||
|
stats Stats
|
||||||
|
|
||||||
|
_closed uint32 // atomic
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Key Features:
|
||||||
|
- Thread-safe connection management using mutexes and atomic operations
|
||||||
|
- Configurable pool size and idle connections
|
||||||
|
- Connection health monitoring with `isHealthyConn()`
|
||||||
|
- Automatic connection cleanup
|
||||||
|
- Connection reuse optimization
|
||||||
|
- Error handling and recovery
|
||||||
|
- FIFO/LIFO connection management based on `PoolFIFO` option
|
||||||
|
- Minimum idle connections maintenance
|
||||||
|
- Maximum active connections enforcement
|
||||||
|
- Connection lifetime and idle timeout management
|
||||||
|
|
||||||
|
Pool Management:
|
||||||
|
- `Get()` - Retrieves a connection from the pool or creates a new one
|
||||||
|
- `Put()` - Returns a connection to the pool
|
||||||
|
- `Remove()` - Removes a connection from the pool
|
||||||
|
- `Close()` - Closes the pool and all connections
|
||||||
|
- `NewConn()` - Creates a new connection
|
||||||
|
- `CloseConn()` - Closes a specific connection
|
||||||
|
- `Len()` - Returns total number of connections
|
||||||
|
- `IdleLen()` - Returns number of idle connections
|
||||||
|
- `Stats()` - Returns pool statistics
|
||||||
|
|
||||||
|
##### 2. SingleConnPool (`internal/pool/pool_single.go`)
|
||||||
|
Implementation:
|
||||||
|
```go
|
||||||
|
type SingleConnPool struct {
|
||||||
|
pool Pooler
|
||||||
|
cn *Conn
|
||||||
|
stickyErr error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use Cases:
|
||||||
|
- Single connection scenarios
|
||||||
|
- Transaction operations
|
||||||
|
- Pub/Sub subscriptions
|
||||||
|
- Pipeline operations
|
||||||
|
- Maintains a single connection with error state tracking
|
||||||
|
|
||||||
|
##### 3. StickyConnPool (`internal/pool/pool_sticky.go`)
|
||||||
|
Implementation:
|
||||||
|
```go
|
||||||
|
type StickyConnPool struct {
|
||||||
|
pool Pooler
|
||||||
|
shared int32 // atomic
|
||||||
|
state uint32 // atomic
|
||||||
|
ch chan *Conn
|
||||||
|
_badConnError atomic.Value
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Connection stickiness for consistent connection usage
|
||||||
|
- State management (default, initialized, closed)
|
||||||
|
- Error handling with `BadConnError`
|
||||||
|
- Thread safety with atomic operations
|
||||||
|
- Connection sharing support
|
||||||
|
- Automatic error recovery
|
||||||
|
|
||||||
|
### 2. Client Types
|
||||||
|
|
||||||
|
#### Base Client
|
||||||
|
Detailed implementation:
|
||||||
|
```go
|
||||||
|
type baseClient struct {
|
||||||
|
opt *Options
|
||||||
|
connPool pool.Pooler
|
||||||
|
onClose func() error // hook called when client is closed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Responsibilities:
|
||||||
|
1. Connection Management
|
||||||
|
- Pool initialization
|
||||||
|
- Connection acquisition
|
||||||
|
- Connection release
|
||||||
|
- Health monitoring
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
2. Command Execution
|
||||||
|
- Protocol handling
|
||||||
|
- Response parsing
|
||||||
|
- Error handling
|
||||||
|
- Retry logic with configurable backoff
|
||||||
|
|
||||||
|
3. Lifecycle Management
|
||||||
|
- Initialization
|
||||||
|
- Cleanup
|
||||||
|
- Resource management
|
||||||
|
- Connection state tracking
|
||||||
|
|
||||||
|
#### Client Types
|
||||||
|
|
||||||
|
##### 1. Client (`redis.go`)
|
||||||
|
Features:
|
||||||
|
- Connection pooling with configurable options
|
||||||
|
- Command execution with retry support
|
||||||
|
- Pipeline support for batch operations
|
||||||
|
- Transaction support with MULTI/EXEC
|
||||||
|
- Error handling and recovery
|
||||||
|
- Resource management
|
||||||
|
- Hooks system for extensibility
|
||||||
|
- Timeout management
|
||||||
|
- Connection health monitoring
|
||||||
|
|
||||||
|
##### 2. Conn (`redis.go`)
|
||||||
|
Features:
|
||||||
|
- Single connection management
|
||||||
|
- Direct command execution
|
||||||
|
- No pooling overhead
|
||||||
|
- Dedicated connection
|
||||||
|
- Transaction support
|
||||||
|
- Pipeline support
|
||||||
|
- Error handling
|
||||||
|
- Connection state tracking
|
||||||
|
|
||||||
|
##### 3. PubSub (`pubsub.go`)
|
||||||
|
Features:
|
||||||
|
- Subscription management
|
||||||
|
- Message handling
|
||||||
|
- Reconnection logic
|
||||||
|
- Channel management
|
||||||
|
- Pattern matching
|
||||||
|
- Thread safety for subscription operations
|
||||||
|
- Automatic resubscription on reconnection
|
||||||
|
- Message channel management
|
||||||
|
|
||||||
|
### 3. Error Handling and Cleanup
|
||||||
|
|
||||||
|
#### Error Types
|
||||||
|
Detailed error handling:
|
||||||
|
```go
|
||||||
|
var (
|
||||||
|
ErrClosed = errors.New("redis: client is closed")
|
||||||
|
ErrPoolExhausted = errors.New("redis: connection pool exhausted")
|
||||||
|
ErrPoolTimeout = errors.New("redis: connection pool timeout")
|
||||||
|
)
|
||||||
|
|
||||||
|
type BadConnError struct {
|
||||||
|
wrapped error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Error Recovery Strategies:
|
||||||
|
1. Connection Errors
|
||||||
|
- Automatic reconnection
|
||||||
|
- Backoff strategies
|
||||||
|
- Health checks
|
||||||
|
- Error state tracking
|
||||||
|
|
||||||
|
2. Protocol Errors
|
||||||
|
- Response parsing
|
||||||
|
- Protocol validation
|
||||||
|
- Error propagation
|
||||||
|
- Recovery mechanisms
|
||||||
|
|
||||||
|
3. Resource Errors
|
||||||
|
- Cleanup procedures
|
||||||
|
- Resource release
|
||||||
|
- State management
|
||||||
|
- Error reporting
|
||||||
|
|
||||||
|
#### Cleanup Process
|
||||||
|
Detailed cleanup:
|
||||||
|
1. Connection Cleanup
|
||||||
|
```go
|
||||||
|
func (p *ConnPool) Close() error {
|
||||||
|
if !atomic.CompareAndSwapUint32(&p._closed, 0, 1) {
|
||||||
|
return ErrClosed
|
||||||
|
}
|
||||||
|
// Cleanup implementation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Resource Management
|
||||||
|
- Connection closure
|
||||||
|
- Buffer cleanup
|
||||||
|
- State reset
|
||||||
|
- Error handling
|
||||||
|
- Resource tracking
|
||||||
|
|
||||||
|
### 4. Hooks System
|
||||||
|
|
||||||
|
#### Implementation Details
|
||||||
|
```go
|
||||||
|
type hooksMixin struct {
|
||||||
|
hooksMu sync.RWMutex
|
||||||
|
current hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
type hooks struct {
|
||||||
|
dial DialHook
|
||||||
|
process ProcessHook
|
||||||
|
pipeline ProcessPipelineHook
|
||||||
|
txPipeline ProcessTxPipelineHook
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Hook Types and Usage:
|
||||||
|
1. `dialHook`
|
||||||
|
- Connection establishment
|
||||||
|
- Authentication
|
||||||
|
- Protocol negotiation
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
2. `processHook`
|
||||||
|
- Command execution
|
||||||
|
- Response handling
|
||||||
|
- Error processing
|
||||||
|
- Metrics collection
|
||||||
|
|
||||||
|
3. `processPipelineHook`
|
||||||
|
- Pipeline execution
|
||||||
|
- Batch processing
|
||||||
|
- Response aggregation
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
4. `processTxPipelineHook`
|
||||||
|
- Transaction management
|
||||||
|
- Command grouping
|
||||||
|
- Atomic execution
|
||||||
|
- Error recovery
|
||||||
|
|
||||||
|
### 5. Configuration
|
||||||
|
|
||||||
|
#### Options Structure
|
||||||
|
Detailed configuration:
|
||||||
|
```go
|
||||||
|
type Options struct {
|
||||||
|
// Network settings
|
||||||
|
Network string
|
||||||
|
Addr string
|
||||||
|
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||||
|
|
||||||
|
// Authentication
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
CredentialsProvider func() (username string, password string)
|
||||||
|
|
||||||
|
// Timeouts
|
||||||
|
DialTimeout time.Duration
|
||||||
|
ReadTimeout time.Duration
|
||||||
|
WriteTimeout time.Duration
|
||||||
|
|
||||||
|
// Pool settings
|
||||||
|
PoolSize int
|
||||||
|
MinIdleConns int
|
||||||
|
MaxIdleConns int
|
||||||
|
MaxActiveConns int
|
||||||
|
PoolTimeout time.Duration
|
||||||
|
|
||||||
|
// TLS
|
||||||
|
TLSConfig *tls.Config
|
||||||
|
|
||||||
|
// Protocol
|
||||||
|
Protocol int
|
||||||
|
ClientName string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuration Management:
|
||||||
|
1. Default Values
|
||||||
|
- Network: "tcp"
|
||||||
|
- Protocol: 3
|
||||||
|
- PoolSize: 10 * runtime.GOMAXPROCS
|
||||||
|
- PoolTimeout: ReadTimeout + 1 second
|
||||||
|
- MinIdleConns: 0
|
||||||
|
- MaxIdleConns: 0
|
||||||
|
- MaxActiveConns: 0 (unlimited)
|
||||||
|
|
||||||
|
2. Validation
|
||||||
|
- Parameter bounds checking
|
||||||
|
- Required field validation
|
||||||
|
- Type validation
|
||||||
|
- Value validation
|
||||||
|
|
||||||
|
3. Dynamic Updates
|
||||||
|
- Runtime configuration changes
|
||||||
|
- Connection pool adjustments
|
||||||
|
- Timeout modifications
|
||||||
|
- Protocol version updates
|
||||||
|
|
||||||
|
4. Environment Integration
|
||||||
|
- URL-based configuration
|
||||||
|
- Environment variable support
|
||||||
|
- Configuration file support
|
||||||
|
- Command-line options
|
||||||
|
|
||||||
|
### 6. Monitoring and Instrumentation
|
||||||
|
|
||||||
|
#### OpenTelemetry Integration
|
||||||
|
The client provides comprehensive monitoring capabilities through OpenTelemetry integration:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Enable tracing instrumentation
|
||||||
|
if err := redisotel.InstrumentTracing(rdb); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable metrics instrumentation
|
||||||
|
if err := redisotel.InstrumentMetrics(rdb); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Distributed tracing
|
||||||
|
- Performance metrics
|
||||||
|
- Connection monitoring
|
||||||
|
- Error tracking
|
||||||
|
- Command execution timing
|
||||||
|
- Resource usage monitoring
|
||||||
|
- Pool statistics
|
||||||
|
- Health checks
|
||||||
|
|
||||||
|
#### Custom Hooks
|
||||||
|
The client supports custom hooks for monitoring and instrumentation:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type redisHook struct{}
|
||||||
|
|
||||||
|
func (redisHook) DialHook(hook redis.DialHook) redis.DialHook {
|
||||||
|
return func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
// Custom monitoring logic
|
||||||
|
return hook(ctx, network, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (redisHook) ProcessHook(hook redis.ProcessHook) redis.ProcessHook {
|
||||||
|
return func(ctx context.Context, cmd redis.Cmder) error {
|
||||||
|
// Custom monitoring logic
|
||||||
|
return hook(ctx, cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (redisHook) ProcessPipelineHook(hook redis.ProcessPipelineHook) redis.ProcessPipelineHook {
|
||||||
|
return func(ctx context.Context, cmds []redis.Cmder) error {
|
||||||
|
// Custom monitoring logic
|
||||||
|
return hook(ctx, cmds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
- Performance monitoring
|
||||||
|
- Debugging
|
||||||
|
- Custom metrics
|
||||||
|
- Error tracking
|
||||||
|
- Resource usage
|
||||||
|
- Connection health
|
||||||
|
- Command patterns
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Connection Management
|
||||||
|
Detailed guidelines:
|
||||||
|
- Pool sizing based on workload
|
||||||
|
- Consider concurrent operations
|
||||||
|
- Account for peak loads
|
||||||
|
- Monitor pool statistics
|
||||||
|
- Adjust based on metrics
|
||||||
|
|
||||||
|
- Connection monitoring
|
||||||
|
- Track connection health
|
||||||
|
- Monitor pool statistics
|
||||||
|
- Watch for errors
|
||||||
|
- Log connection events
|
||||||
|
|
||||||
|
- Health checks
|
||||||
|
- Regular connection validation
|
||||||
|
- Error detection
|
||||||
|
- Automatic recovery
|
||||||
|
- State monitoring
|
||||||
|
|
||||||
|
- Resource limits
|
||||||
|
- Set appropriate pool sizes
|
||||||
|
- Configure timeouts
|
||||||
|
- Monitor resource usage
|
||||||
|
- Implement circuit breakers
|
||||||
|
|
||||||
|
- Timeout configuration
|
||||||
|
- Set appropriate timeouts
|
||||||
|
- Consider network conditions
|
||||||
|
- Account for operation types
|
||||||
|
- Monitor timeout events
|
||||||
|
|
||||||
|
### 2. Error Handling
|
||||||
|
Implementation strategies:
|
||||||
|
- Error recovery
|
||||||
|
- Automatic retries
|
||||||
|
- Backoff strategies
|
||||||
|
- Error classification
|
||||||
|
- Recovery procedures
|
||||||
|
|
||||||
|
- Retry logic
|
||||||
|
- Configurable attempts
|
||||||
|
- Exponential backoff
|
||||||
|
- Error filtering
|
||||||
|
- State preservation
|
||||||
|
|
||||||
|
- Circuit breakers
|
||||||
|
- Error threshold monitoring
|
||||||
|
- State management
|
||||||
|
- Recovery procedures
|
||||||
|
- Health checks
|
||||||
|
|
||||||
|
- Monitoring
|
||||||
|
- Error tracking
|
||||||
|
- Performance metrics
|
||||||
|
- Resource usage
|
||||||
|
- Health status
|
||||||
|
|
||||||
|
- Logging
|
||||||
|
- Error details
|
||||||
|
- Context information
|
||||||
|
- Stack traces
|
||||||
|
- Performance data
|
||||||
|
|
||||||
|
### 3. Resource Cleanup
|
||||||
|
Cleanup procedures:
|
||||||
|
- Connection closure
|
||||||
|
- Proper cleanup
|
||||||
|
- Error handling
|
||||||
|
- State management
|
||||||
|
- Resource release
|
||||||
|
|
||||||
|
- Resource release
|
||||||
|
- Memory cleanup
|
||||||
|
- File handle closure
|
||||||
|
- Network cleanup
|
||||||
|
- State reset
|
||||||
|
|
||||||
|
- State management
|
||||||
|
- Connection state
|
||||||
|
- Pool state
|
||||||
|
- Error state
|
||||||
|
- Resource state
|
||||||
|
|
||||||
|
- Error handling
|
||||||
|
- Error propagation
|
||||||
|
- Cleanup on error
|
||||||
|
- State recovery
|
||||||
|
- Resource cleanup
|
||||||
|
|
||||||
|
- Monitoring
|
||||||
|
- Resource usage
|
||||||
|
- Cleanup events
|
||||||
|
- Error tracking
|
||||||
|
- Performance impact
|
||||||
|
|
||||||
|
### 4. Performance Optimization
|
||||||
|
Optimization techniques:
|
||||||
|
- Connection pooling
|
||||||
|
- Efficient reuse
|
||||||
|
- Load balancing
|
||||||
|
- Health monitoring
|
||||||
|
- Resource management
|
||||||
|
|
||||||
|
- Pipeline usage
|
||||||
|
- Batch operations
|
||||||
|
- Reduced round trips
|
||||||
|
- Improved throughput
|
||||||
|
- Resource efficiency
|
||||||
|
|
||||||
|
- Batch operations
|
||||||
|
- Command grouping
|
||||||
|
- Reduced overhead
|
||||||
|
- Improved performance
|
||||||
|
- Resource efficiency
|
||||||
|
|
||||||
|
- Resource management
|
||||||
|
- Efficient allocation
|
||||||
|
- Proper cleanup
|
||||||
|
- Monitoring
|
||||||
|
- Optimization
|
||||||
|
|
||||||
|
- Monitoring
|
||||||
|
- Performance metrics
|
||||||
|
- Resource usage
|
||||||
|
- Bottleneck detection
|
||||||
|
- Optimization opportunities
|
||||||
|
|
||||||
|
## Diagrams
|
||||||
|
|
||||||
|
### Connection Pool Architecture
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ Client │────▶│ ConnPool │────▶│ Conn │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────┐
|
||||||
|
│ PoolStats │
|
||||||
|
└─────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling Flow
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ Command │────▶│ Execution │────▶│ Error Check │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌─────────────┐ ┌─────────────┐
|
||||||
|
│ Success │ │ Recovery │
|
||||||
|
└─────────────┘ └─────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook System
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ Client │────▶│ Hooks │────▶│ Execution │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────┐
|
||||||
|
│ Custom │
|
||||||
|
│ Behavior │
|
||||||
|
└─────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Connection Lifecycle
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ Creation │────▶│ Active │────▶│ Cleanup │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ Init │ │ Usage │ │ Release │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pool Management
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ Get │────▶│ Use │────▶│ Put │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ Checkout │ │ Monitor │ │ Return │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known Issues and Areas of Improvement
|
||||||
|
|
||||||
|
### 1. Performance Considerations
|
||||||
|
|
||||||
|
#### Potential Performance Bottlenecks
|
||||||
|
|
||||||
|
1. **Connection Pool Management**
|
||||||
|
- Lock contention in connection pool operations
|
||||||
|
- Inefficient connection reuse strategies
|
||||||
|
- Suboptimal pool sizing algorithms
|
||||||
|
- High overhead in connection health checks
|
||||||
|
|
||||||
|
2. **Memory Management**
|
||||||
|
- Buffer allocation/deallocation overhead
|
||||||
|
- Memory fragmentation in long-running applications
|
||||||
|
- Inefficient buffer reuse strategies
|
||||||
|
- Potential memory leaks in edge cases
|
||||||
|
|
||||||
|
3. **Protocol Handling**
|
||||||
|
- RESP parsing overhead
|
||||||
|
- Inefficient command serialization
|
||||||
|
- Suboptimal batch processing
|
||||||
|
- Redundant protocol validations
|
||||||
|
|
||||||
|
4. **Concurrency Issues**
|
||||||
|
- Lock contention in shared resources
|
||||||
|
- Inefficient atomic operations
|
||||||
|
- Suboptimal goroutine management
|
||||||
|
- Race conditions in edge cases
|
||||||
|
|
||||||
|
### 2. Known Issues
|
||||||
|
|
||||||
|
1. **Connection Management**
|
||||||
|
- Occasional connection leaks under high load
|
||||||
|
- Suboptimal connection reuse in certain scenarios
|
||||||
|
- Race conditions in connection pool management
|
||||||
|
- Inefficient connection cleanup in edge cases
|
||||||
|
|
||||||
|
2. **Error Handling**
|
||||||
|
- Overly aggressive error recovery
|
||||||
|
- Suboptimal retry strategies
|
||||||
|
- Incomplete error context propagation
|
||||||
|
- Inconsistent error handling patterns
|
||||||
|
|
||||||
|
3. **Resource Management**
|
||||||
|
- Memory usage spikes under certain conditions
|
||||||
|
- Suboptimal buffer management
|
||||||
|
- Inefficient resource cleanup
|
||||||
|
- Potential resource leaks in edge cases
|
||||||
|
|
||||||
|
4. **Protocol Implementation**
|
||||||
|
- Inefficient command serialization
|
||||||
|
- Suboptimal response parsing
|
||||||
|
- Redundant protocol validations
|
||||||
|
- Incomplete protocol feature support
|
||||||
|
|
||||||
|
### 3. Areas for Improvement
|
||||||
|
|
||||||
|
1. **Performance Optimization**
|
||||||
|
- Implement connection pooling optimizations
|
||||||
|
- Optimize buffer management
|
||||||
|
- Improve protocol handling efficiency
|
||||||
|
- Enhance concurrency patterns
|
||||||
|
|
||||||
|
2. **Resource Management**
|
||||||
|
- Implement more efficient memory management
|
||||||
|
- Optimize resource cleanup
|
||||||
|
- Improve connection reuse strategies
|
||||||
|
- Enhance buffer reuse patterns
|
||||||
|
|
||||||
|
3. **Error Handling**
|
||||||
|
- Implement more sophisticated retry strategies
|
||||||
|
- Improve error context propagation
|
||||||
|
- Enhance error recovery mechanisms
|
||||||
|
- Standardize error handling patterns
|
||||||
|
|
||||||
|
4. **Protocol Implementation**
|
||||||
|
- Optimize command serialization
|
||||||
|
- Improve response parsing efficiency
|
||||||
|
- Reduce protocol validation overhead
|
||||||
|
- Enhance protocol feature support
|
||||||
|
|
||||||
|
5. **Monitoring and Diagnostics**
|
||||||
|
- Implement comprehensive metrics
|
||||||
|
- Enhance logging capabilities
|
||||||
|
- Improve debugging support
|
||||||
|
- Add performance profiling tools
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
While the current implementation provides a robust and feature-rich Redis client, there are several areas where performance and reliability can be improved. The focus should be on:
|
||||||
|
|
||||||
|
1. Optimizing critical paths
|
||||||
|
2. Improving resource management
|
||||||
|
3. Enhancing error handling
|
||||||
|
4. Implementing better monitoring
|
||||||
|
5. Reducing overhead in common operations
|
||||||
|
|
||||||
|
These improvements will help make the client more competitive with other implementations while maintaining its current strengths in reliability and feature completeness.
|
164
docs/redis_command_processing.md
Normal file
164
docs/redis_command_processing.md
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
# Redis Command Processing
|
||||||
|
|
||||||
|
This document describes how commands are processed in the Redis client, including the command pipeline, error handling, and various command execution modes.
|
||||||
|
|
||||||
|
## Command Interface
|
||||||
|
|
||||||
|
The core of command processing is the `Cmder` interface:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Cmder interface {
|
||||||
|
Name() string // Command name (e.g., "set", "get")
|
||||||
|
FullName() string // Full command name (e.g., "cluster info")
|
||||||
|
Args() []interface{} // Command arguments
|
||||||
|
String() string // String representation of command and response
|
||||||
|
readTimeout() *time.Duration
|
||||||
|
readReply(rd *proto.Reader) error
|
||||||
|
SetErr(error)
|
||||||
|
Err() error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Command Processing Pipeline
|
||||||
|
|
||||||
|
### 1. Command Creation
|
||||||
|
- Commands are created using factory functions (e.g., `NewCmd`, `NewStatusCmd`)
|
||||||
|
- Each command type implements the `Cmder` interface
|
||||||
|
- Commands can specify read timeouts and key positions
|
||||||
|
|
||||||
|
### 2. Command Execution
|
||||||
|
The execution flow:
|
||||||
|
1. Command validation
|
||||||
|
2. Connection acquisition from pool
|
||||||
|
3. Command writing to Redis
|
||||||
|
4. Response reading
|
||||||
|
5. Error handling and retries
|
||||||
|
|
||||||
|
### 3. Error Handling
|
||||||
|
- Network errors trigger retries based on configuration
|
||||||
|
- Redis errors are returned directly
|
||||||
|
- Timeout handling with configurable backoff
|
||||||
|
|
||||||
|
## Command Execution Modes
|
||||||
|
|
||||||
|
### 1. Single Command
|
||||||
|
```go
|
||||||
|
err := client.Process(ctx, cmd)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Pipeline
|
||||||
|
```go
|
||||||
|
pipe := client.Pipeline()
|
||||||
|
pipe.Process(ctx, cmd1)
|
||||||
|
pipe.Process(ctx, cmd2)
|
||||||
|
cmds, err := pipe.Exec(ctx)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Transaction Pipeline
|
||||||
|
```go
|
||||||
|
pipe := client.TxPipeline()
|
||||||
|
pipe.Process(ctx, cmd1)
|
||||||
|
pipe.Process(ctx, cmd2)
|
||||||
|
cmds, err := pipe.Exec(ctx)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Command Types
|
||||||
|
|
||||||
|
### 1. Basic Commands
|
||||||
|
- String commands (SET, GET)
|
||||||
|
- Hash commands (HGET, HSET)
|
||||||
|
- List commands (LPUSH, RPOP)
|
||||||
|
- Set commands (SADD, SMEMBERS)
|
||||||
|
- Sorted Set commands (ZADD, ZRANGE)
|
||||||
|
|
||||||
|
### 2. Advanced Commands
|
||||||
|
- Scripting (EVAL, EVALSHA)
|
||||||
|
- Pub/Sub (SUBSCRIBE, PUBLISH)
|
||||||
|
- Transactions (MULTI, EXEC)
|
||||||
|
- Cluster commands (CLUSTER INFO)
|
||||||
|
|
||||||
|
### 3. Specialized Commands
|
||||||
|
- Search commands (FT.SEARCH)
|
||||||
|
- JSON commands (JSON.SET, JSON.GET)
|
||||||
|
- Time Series commands (TS.ADD, TS.RANGE)
|
||||||
|
- Probabilistic data structures (BF.ADD, CF.ADD)
|
||||||
|
|
||||||
|
## Command Processing in Different Clients
|
||||||
|
|
||||||
|
### 1. Standalone Client
|
||||||
|
- Direct command execution
|
||||||
|
- Connection pooling
|
||||||
|
- Automatic retries
|
||||||
|
|
||||||
|
### 2. Cluster Client
|
||||||
|
- Command routing based on key slots
|
||||||
|
- MOVED/ASK redirection handling
|
||||||
|
- Cross-slot command batching
|
||||||
|
|
||||||
|
### 3. Ring Client
|
||||||
|
- Command sharding based on key hashing
|
||||||
|
- Consistent hashing for node selection
|
||||||
|
- Parallel command execution
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Command Batching**
|
||||||
|
- Use pipelines for multiple commands
|
||||||
|
- Batch related commands together
|
||||||
|
- Consider transaction pipelines for atomic operations
|
||||||
|
|
||||||
|
2. **Error Handling**
|
||||||
|
- Check command errors after execution
|
||||||
|
- Handle network errors appropriately
|
||||||
|
- Use retries for transient failures
|
||||||
|
|
||||||
|
3. **Performance**
|
||||||
|
- Use appropriate command types
|
||||||
|
- Leverage pipelining for bulk operations
|
||||||
|
- Monitor command execution times
|
||||||
|
|
||||||
|
4. **Resource Management**
|
||||||
|
- Close connections properly
|
||||||
|
- Use context for timeouts
|
||||||
|
- Monitor connection pool usage
|
||||||
|
|
||||||
|
## Common Issues and Solutions
|
||||||
|
|
||||||
|
1. **Timeout Handling**
|
||||||
|
- Configure appropriate timeouts
|
||||||
|
- Use context for cancellation
|
||||||
|
- Implement retry strategies
|
||||||
|
|
||||||
|
2. **Connection Issues**
|
||||||
|
- Monitor connection pool health
|
||||||
|
- Handle connection failures gracefully
|
||||||
|
- Implement proper cleanup
|
||||||
|
|
||||||
|
3. **Command Errors**
|
||||||
|
- Validate commands before execution
|
||||||
|
- Handle Redis-specific errors
|
||||||
|
- Implement proper error recovery
|
||||||
|
|
||||||
|
## Monitoring and Debugging
|
||||||
|
|
||||||
|
1. **Command Monitoring**
|
||||||
|
- Use SLOWLOG for performance analysis
|
||||||
|
- Monitor command execution times
|
||||||
|
- Track error rates
|
||||||
|
|
||||||
|
2. **Client Information**
|
||||||
|
- Monitor client connections
|
||||||
|
- Track command usage patterns
|
||||||
|
- Analyze performance bottlenecks
|
||||||
|
|
||||||
|
## Future Improvements
|
||||||
|
|
||||||
|
1. **Command Processing**
|
||||||
|
- Enhanced error handling
|
||||||
|
- Improved retry mechanisms
|
||||||
|
- Better connection management
|
||||||
|
|
||||||
|
2. **Performance**
|
||||||
|
- Optimized command batching
|
||||||
|
- Enhanced pipelining
|
||||||
|
- Better resource utilization
|
271
docs/redis_pool.md
Normal file
271
docs/redis_pool.md
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
# Redis Connection Pool Implementation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Redis client implements a sophisticated connection pooling mechanism to efficiently manage Redis connections. This document details the implementation, features, and behavior of the connection pool system.
|
||||||
|
|
||||||
|
## Core Components
|
||||||
|
|
||||||
|
### 1. Connection Interface (`Pooler`)
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Pooler interface {
|
||||||
|
NewConn(context.Context) (*Conn, error)
|
||||||
|
CloseConn(*Conn) error
|
||||||
|
|
||||||
|
Get(context.Context) (*Conn, error)
|
||||||
|
Put(context.Context, *Conn)
|
||||||
|
Remove(context.Context, *Conn, error)
|
||||||
|
|
||||||
|
Len() int
|
||||||
|
IdleLen() int
|
||||||
|
Stats() *Stats
|
||||||
|
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Pooler` interface defines the contract for all connection pool implementations:
|
||||||
|
- Connection lifecycle management
|
||||||
|
- Connection acquisition and release
|
||||||
|
- Pool statistics and monitoring
|
||||||
|
- Resource cleanup
|
||||||
|
|
||||||
|
### 2. Connection Pool Options
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Options struct {
|
||||||
|
Dialer func(context.Context) (net.Conn, error)
|
||||||
|
|
||||||
|
PoolFIFO bool
|
||||||
|
PoolSize int
|
||||||
|
DialTimeout time.Duration
|
||||||
|
PoolTimeout time.Duration
|
||||||
|
MinIdleConns int
|
||||||
|
MaxIdleConns int
|
||||||
|
MaxActiveConns int
|
||||||
|
ConnMaxIdleTime time.Duration
|
||||||
|
ConnMaxLifetime time.Duration
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Key configuration parameters:
|
||||||
|
- `PoolFIFO`: Use FIFO mode for connection pool GET/PUT (default LIFO)
|
||||||
|
- `PoolSize`: Base number of connections (default: 10 * runtime.GOMAXPROCS)
|
||||||
|
- `MinIdleConns`: Minimum number of idle connections
|
||||||
|
- `MaxIdleConns`: Maximum number of idle connections
|
||||||
|
- `MaxActiveConns`: Maximum number of active connections
|
||||||
|
- `ConnMaxIdleTime`: Maximum idle time for connections
|
||||||
|
- `ConnMaxLifetime`: Maximum lifetime for connections
|
||||||
|
|
||||||
|
### 3. Connection Pool Statistics
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Stats struct {
|
||||||
|
Hits uint32 // number of times free connection was found in the pool
|
||||||
|
Misses uint32 // number of times free connection was NOT found in the pool
|
||||||
|
Timeouts uint32 // number of times a wait timeout occurred
|
||||||
|
|
||||||
|
TotalConns uint32 // number of total connections in the pool
|
||||||
|
IdleConns uint32 // number of idle connections in the pool
|
||||||
|
StaleConns uint32 // number of stale connections removed from the pool
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Main Connection Pool Implementation (`ConnPool`)
|
||||||
|
|
||||||
|
#### Structure
|
||||||
|
```go
|
||||||
|
type ConnPool struct {
|
||||||
|
cfg *Options
|
||||||
|
|
||||||
|
dialErrorsNum uint32 // atomic
|
||||||
|
lastDialError atomic.Value
|
||||||
|
|
||||||
|
queue chan struct{}
|
||||||
|
|
||||||
|
connsMu sync.Mutex
|
||||||
|
conns []*Conn
|
||||||
|
idleConns []*Conn
|
||||||
|
|
||||||
|
poolSize int
|
||||||
|
idleConnsLen int
|
||||||
|
|
||||||
|
stats Stats
|
||||||
|
|
||||||
|
_closed uint32 // atomic
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Key Features
|
||||||
|
1. **Thread Safety**
|
||||||
|
- Mutex-protected connection lists
|
||||||
|
- Atomic operations for counters
|
||||||
|
- Thread-safe connection management
|
||||||
|
|
||||||
|
2. **Connection Management**
|
||||||
|
- Automatic connection creation
|
||||||
|
- Connection reuse
|
||||||
|
- Connection cleanup
|
||||||
|
- Health checks
|
||||||
|
|
||||||
|
3. **Resource Control**
|
||||||
|
- Maximum connection limits
|
||||||
|
- Idle connection management
|
||||||
|
- Connection lifetime control
|
||||||
|
- Timeout handling
|
||||||
|
|
||||||
|
4. **Error Handling**
|
||||||
|
- Connection error tracking
|
||||||
|
- Automatic error recovery
|
||||||
|
- Error propagation
|
||||||
|
- Connection validation
|
||||||
|
|
||||||
|
### 5. Single Connection Pool (`SingleConnPool`)
|
||||||
|
|
||||||
|
```go
|
||||||
|
type SingleConnPool struct {
|
||||||
|
pool Pooler
|
||||||
|
cn *Conn
|
||||||
|
stickyErr error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use cases:
|
||||||
|
- Single connection scenarios
|
||||||
|
- Transaction operations
|
||||||
|
- Pub/Sub subscriptions
|
||||||
|
- Pipeline operations
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Dedicated connection management
|
||||||
|
- Error state tracking
|
||||||
|
- Connection reuse
|
||||||
|
- Resource cleanup
|
||||||
|
|
||||||
|
### 6. Sticky Connection Pool (`StickyConnPool`)
|
||||||
|
|
||||||
|
```go
|
||||||
|
type StickyConnPool struct {
|
||||||
|
pool Pooler
|
||||||
|
shared int32 // atomic
|
||||||
|
state uint32 // atomic
|
||||||
|
ch chan *Conn
|
||||||
|
_badConnError atomic.Value
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Connection stickiness
|
||||||
|
- State management
|
||||||
|
- Error handling
|
||||||
|
- Thread safety
|
||||||
|
- Connection sharing
|
||||||
|
|
||||||
|
### 7. Connection Health Checks
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (p *ConnPool) isHealthyConn(cn *Conn) bool {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
if p.cfg.ConnMaxLifetime > 0 && now.Sub(cn.createdAt) >= p.cfg.ConnMaxLifetime {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if p.cfg.ConnMaxIdleTime > 0 && now.Sub(cn.UsedAt()) >= p.cfg.ConnMaxIdleTime {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if connCheck(cn.netConn) != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
cn.SetUsedAt(now)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Health check criteria:
|
||||||
|
- Connection lifetime
|
||||||
|
- Idle time
|
||||||
|
- Network connectivity
|
||||||
|
- Protocol state
|
||||||
|
|
||||||
|
### 8. Error Types
|
||||||
|
|
||||||
|
```go
|
||||||
|
var (
|
||||||
|
ErrClosed = errors.New("redis: client is closed")
|
||||||
|
ErrPoolExhausted = errors.New("redis: connection pool exhausted")
|
||||||
|
ErrPoolTimeout = errors.New("redis: connection pool timeout")
|
||||||
|
)
|
||||||
|
|
||||||
|
type BadConnError struct {
|
||||||
|
wrapped error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9. Best Practices
|
||||||
|
|
||||||
|
1. **Pool Configuration**
|
||||||
|
- Set appropriate pool size based on workload
|
||||||
|
- Configure timeouts based on network conditions
|
||||||
|
- Monitor pool statistics
|
||||||
|
- Adjust idle connection settings
|
||||||
|
|
||||||
|
2. **Connection Management**
|
||||||
|
- Proper connection cleanup
|
||||||
|
- Error handling
|
||||||
|
- Resource limits
|
||||||
|
- Health monitoring
|
||||||
|
|
||||||
|
3. **Performance Optimization**
|
||||||
|
- Connection reuse
|
||||||
|
- Efficient pooling
|
||||||
|
- Resource cleanup
|
||||||
|
- Error recovery
|
||||||
|
|
||||||
|
4. **Monitoring**
|
||||||
|
- Track pool statistics
|
||||||
|
- Monitor connection health
|
||||||
|
- Watch for errors
|
||||||
|
- Resource usage
|
||||||
|
|
||||||
|
### 10. Known Issues and Limitations
|
||||||
|
|
||||||
|
1. **Performance Considerations**
|
||||||
|
- Lock contention in high-concurrency scenarios
|
||||||
|
- Connection creation overhead
|
||||||
|
- Resource cleanup impact
|
||||||
|
- Memory usage
|
||||||
|
|
||||||
|
2. **Resource Management**
|
||||||
|
- Connection leaks in edge cases
|
||||||
|
- Resource cleanup timing
|
||||||
|
- Memory fragmentation
|
||||||
|
- Network resource usage
|
||||||
|
|
||||||
|
3. **Error Handling**
|
||||||
|
- Error recovery strategies
|
||||||
|
- Connection validation
|
||||||
|
- Error propagation
|
||||||
|
- State management
|
||||||
|
|
||||||
|
### 11. Future Improvements
|
||||||
|
|
||||||
|
1. **Performance**
|
||||||
|
- Optimize lock contention
|
||||||
|
- Improve connection reuse
|
||||||
|
- Enhance resource cleanup
|
||||||
|
- Better memory management
|
||||||
|
|
||||||
|
2. **Features**
|
||||||
|
- Enhanced monitoring
|
||||||
|
- Better error handling
|
||||||
|
- Improved resource management
|
||||||
|
- Advanced connection validation
|
||||||
|
|
||||||
|
3. **Reliability**
|
||||||
|
- Better error recovery
|
||||||
|
- Enhanced health checks
|
||||||
|
- Improved state management
|
||||||
|
- Better resource cleanup
|
146
docs/redis_testing.md
Normal file
146
docs/redis_testing.md
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
# Redis Testing Guide
|
||||||
|
|
||||||
|
## Running Tests
|
||||||
|
|
||||||
|
### 1. Setup Test Environment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start Docker containers for testing
|
||||||
|
make docker.start
|
||||||
|
|
||||||
|
# Stop Docker containers when done
|
||||||
|
make docker.stop
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Environment Variables
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Redis version and image configuration
|
||||||
|
CLIENT_LIBS_TEST_IMAGE=redislabs/client-libs-test:rs-7.4.0-v2 # Default Redis Stack image
|
||||||
|
REDIS_VERSION=7.2 # Default Redis version
|
||||||
|
|
||||||
|
# Cluster configuration
|
||||||
|
RE_CLUSTER=false # Set to true for RE testing
|
||||||
|
RCE_DOCKER=false # Set to true for Docker-based Redis CE testing
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
|
||||||
|
# Run tests with race detection, as executed in the CI
|
||||||
|
make test.ci
|
||||||
|
|
||||||
|
|
||||||
|
### 4. Test Coverage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate coverage report
|
||||||
|
go test -coverprofile=coverage.out
|
||||||
|
|
||||||
|
# View coverage report in browser
|
||||||
|
go tool cover -html=coverage.out
|
||||||
|
```
|
||||||
|
|
||||||
|
## Writing Tests
|
||||||
|
|
||||||
|
### 1. Basic Test Structure
|
||||||
|
|
||||||
|
```go
|
||||||
|
package redis_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/bsm/ginkgo/v2"
|
||||||
|
. "github.com/bsm/gomega"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Redis Client", func() {
|
||||||
|
var client *redis.Client
|
||||||
|
var ctx = context.Background()
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
client = redis.NewClient(&redis.Options{
|
||||||
|
Addr: ":6379",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
client.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should handle basic operations", func() {
|
||||||
|
err := client.Set(ctx, "key", "value", 0).Err()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
val, err := client.Get(ctx, "key").Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal("value"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Test Organization
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Use Describe for test groups
|
||||||
|
Describe("Redis Client", func() {
|
||||||
|
// Use Context for different scenarios
|
||||||
|
Context("when connection is established", func() {
|
||||||
|
// Use It for individual test cases
|
||||||
|
It("should handle basic operations", func() {
|
||||||
|
// Test implementation
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Common Test Patterns
|
||||||
|
|
||||||
|
#### Testing Success Cases
|
||||||
|
```go
|
||||||
|
It("should succeed", func() {
|
||||||
|
err := client.Set(ctx, "key", "value", 0).Err()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Testing Error Cases
|
||||||
|
```go
|
||||||
|
It("should return error", func() {
|
||||||
|
_, err := client.Get(ctx, "nonexistent").Result()
|
||||||
|
Expect(err).To(Equal(redis.Nil))
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Testing Timeouts
|
||||||
|
```go
|
||||||
|
It("should timeout", func() {
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err := client.Ping(ctx).Err()
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Best Practices
|
||||||
|
|
||||||
|
1. **Test Structure**
|
||||||
|
- Use descriptive test names
|
||||||
|
- Group related tests together
|
||||||
|
- Keep tests focused and simple
|
||||||
|
- Clean up resources in AfterEach
|
||||||
|
|
||||||
|
2. **Assertions**
|
||||||
|
- Use Gomega's Expect syntax
|
||||||
|
- Be specific in assertions
|
||||||
|
- Test both success and failure cases
|
||||||
|
- Include error checking
|
||||||
|
|
||||||
|
3. **Resource Management**
|
||||||
|
- Close connections in AfterEach
|
||||||
|
- Clean up test data
|
||||||
|
- Handle timeouts properly
|
||||||
|
- Manage test isolation
|
Reference in New Issue
Block a user