Pool Package

A high-performance, reliable network connection pool management system for Go applications.
Features
- Thread-safe connection management with mutex protection
- Support for both client and server connection pools
- Dynamic capacity adjustment based on usage patterns
- Automatic connection health monitoring
- Multiple TLS security modes (none, self-signed, verified)
- Connection identification and tracking
- Graceful error handling and recovery
- Configurable connection creation intervals
- Auto-reconnection with exponential backoff
- Connection activity validation
Installation
go get github.com/NodePassProject/pool
Usage
Client Connection Pool
package main
import (
"net"
"time"
"github.com/NodePassProject/pool"
)
func main() {
// Create a dialer function
dialer := func() (net.Conn, error) {
return net.Dial("tcp", "example.com:8080")
}
// Create a new client pool with:
// - Minimum capacity: 5 connections
// - Maximum capacity: 20 connections
// - Minimum interval: 500ms between connection attempts
// - Maximum interval: 5s between connection attempts
// - TLS mode: "2" (verified certificates)
// - Hostname for certificate verification: "example.com"
clientPool := pool.NewClientPool(
5, 20,
500*time.Millisecond, 5*time.Second,
"2", "example.com",
dialer,
)
// Start the client manager (usually in a goroutine)
go clientPool.ClientManager()
// Get a connection by ID (usually received from the server)
conn := clientPool.ClientGet("connection-id")
// Use the connection...
// When finished with the pool
clientPool.Close()
}
Server Connection Pool
package main
import (
"crypto/tls"
"net"
"github.com/NodePassProject/pool"
)
func main() {
// Create a listener
listener, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
// Optional: Create a TLS config
tlsConfig := &tls.Config{
// Configure TLS settings
MinVersion: tls.VersionTLS13,
}
// Create a new server pool
// - Restrict to specific client IP (optional, "" for any IP, "192.168.1.10" to only allow that specific IP)
// - Use TLS config (optional, nil for no TLS)
// - Use the created listener
serverPool := pool.NewServerPool("192.168.1.10", tlsConfig, listener)
// Start the server manager (usually in a goroutine)
go serverPool.ServerManager()
// Get a new connection from the pool (blocks until available)
id, conn := serverPool.ServerGet()
// Use the connection...
// When finished with the pool
serverPool.Close()
}
Managing Pool Health
// Check if the pool is ready
if clientPool.Ready() {
// The pool is initialized and ready for use
}
// Get current active connection count
activeConnections := clientPool.Active()
// Get current capacity setting
capacity := clientPool.Capacity()
// Get current connection creation interval
interval := clientPool.Interval()
// Manually flush all connections (rarely needed)
clientPool.Flush()
// Record an error (increases internal error counter)
clientPool.AddError()
Security Features
Client IP Restriction
The NewServerPool function allows you to restrict incoming connections to a specific client IP address:
// Create a server pool that only accepts connections from 192.168.1.10
serverPool := pool.NewServerPool("192.168.1.10", tlsConfig, listener)
When the clientIP parameter is set:
- All connections from other IP addresses will be immediately closed
- This provides an additional layer of security beyond network firewalls
- Particularly useful for internal services or dedicated client-server applications
To allow connections from any IP address, use an empty string:
// Create a server pool that accepts connections from any IP
serverPool := pool.NewServerPool("", tlsConfig, listener)
TLS Security Modes
The pool supports three TLS security modes for client connections:
"0": No TLS (plain TCP connections)
"1": Self-signed certificates (InsecureSkipVerify=true)
"2": Verified certificates (proper certificate validation)
Dynamic Adjustment
The pool automatically adjusts:
- Connection creation intervals based on idle connection count
- Connection capacity based on connection creation success rate
Advanced Usage
Custom Error Handling
package main
import (
"log"
"net"
"time"
"github.com/NodePassProject/pool"
"github.com/NodePassProject/logs"
)
func main() {
logger := logs.NewLogger(logs.Info, true)
clientPool := pool.NewClientPool(
5, 20,
500*time.Millisecond, 5*time.Second,
"2", "example.com",
func() (net.Conn, error) {
conn, err := net.Dial("tcp", "example.com:8080")
if err != nil {
// Log the error
logger.Error("Connection failed: %v", err)
// Record the error in the pool
clientPool.AddError()
}
return conn, err
},
)
go clientPool.ClientManager()
// Your application logic...
}
Working with Context
package main
import (
"context"
"net"
"time"
"github.com/NodePassProject/pool"
)
func main() {
// Create a context that can be cancelled
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
clientPool := pool.NewClientPool(
5, 20,
500*time.Millisecond, 5*time.Second,
"2", "example.com",
func() (net.Conn, error) {
// Use context-aware dialer
dialer := net.Dialer{Timeout: 5 * time.Second}
return dialer.DialContext(ctx, "tcp", "example.com:8080")
},
)
go clientPool.ClientManager()
// When needed to stop the pool:
// cancel()
// clientPool.Close()
}
Load Balancing with Multiple Pools
package main
import (
"net"
"sync/atomic"
"time"
"github.com/NodePassProject/pool"
)
func main() {
// Create pools for different servers
serverAddresses := []string{
"server1.example.com:8080",
"server2.example.com:8080",
"server3.example.com:8080",
}
pools := make([]*pool.Pool, len(serverAddresses))
for i, addr := range serverAddresses {
serverAddr := addr // Create local copy for closure
pools[i] = pool.NewClientPool(
5, 20,
500*time.Millisecond, 5*time.Second,
"2", serverAddr[:len(serverAddr)-5], // Extract hostname
func() (net.Conn, error) {
return net.Dial("tcp", serverAddr)
},
)
go pools[i].ClientManager()
}
// Simple round-robin load balancer
var counter int32 = 0
getNextPool := func() *pool.Pool {
next := atomic.AddInt32(&counter, 1) % int32(len(pools))
return pools[next]
}
// Usage
id, conn := getNextPool().ServerGet()
// Use connection...
// When done with all pools
for _, p := range pools {
p.Close()
}
}
Connection Pool Sizing
The ideal pool size depends on your application's workload:
- Too small: Can cause connection contention and delays
- Too large: Wastes resources and may overload the server
Start with a minimum capacity that can handle your baseline load and a maximum capacity that can handle peak loads. Monitor connection usage and adjust accordingly.
TLS Overhead
TLS connections have additional overhead:
- Handshake cost: TLS handshakes are CPU-intensive
- Memory usage: TLS connections require more memory
- Latency: Initial connection setup is slower
If maximum performance is critical and the network is secure, consider using mode "0" (no TLS).
Connection Validation
The isActive method checks connection health by setting a brief read deadline and attempting to read. This ensures connections in the pool are valid, but adds a small overhead. For extremely high-throughput systems, consider implementing a custom validation strategy.
Troubleshooting
Common Issues
-
Connection Timeout
- Check network connectivity
- Verify server address and port
- Increase connection timeout in dialer
-
TLS Handshake Failure
- Verify certificate validity
- Check hostname configuration matches certificate
- For testing, try TLS mode
"1" (InsecureSkipVerify)
-
Pool Exhaustion
- Increase max capacity
- Decrease connection hold time
- Check for connection leaks (connections not being released)
-
High Error Rate
- Monitor with
AddError()
- Implement backoff strategy in dialer
- Consider server-side issues
Debugging
Set log points at key locations:
- Before/after dialer calls
- When connections are added/removed from the pool
- When pool capacity is adjusted
- When connections are validated
License
Copyright (c) 2025, NodePassProject. Licensed under the BSD 3-Clause License.
See the LICENSE file for details.