pool

package
v1.19.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 4, 2026 License: MIT Imports: 15 Imported by: 0

README

HTTP Server Pool Manager

License Go Version Coverage

Thread-safe pool manager for multiple HTTP servers with unified lifecycle control, advanced filtering, and monitoring integration.


Table of Contents


Overview

The pool package provides unified management for multiple HTTP server instances through a thread-safe pool abstraction. It enables simultaneous operation of servers with different configurations while providing centralized lifecycle control, advanced filtering capabilities, and integrated monitoring.

Design Philosophy
  1. Unified Management: Control multiple heterogeneous servers as a single logical unit.
  2. Thread Safety First: All operations protected by sync.RWMutex for concurrent safety.
  3. Flexibility: Support for dynamic server addition, removal, and configuration updates.
  4. Observability: Built-in monitoring and health check integration for all pooled servers.
  5. Error Aggregation: Collect and report errors from all servers systematically.
Key Features
  • Unified Lifecycle: Start, stop, and restart all servers with single operations.
  • Thread-Safe Operations: Concurrent-safe server management using sync.RWMutex.
  • Advanced Filtering: Query servers by name, bind address, or expose address with regex support.
  • Dynamic Management: Add, remove, and update servers during operation without downtime.
  • Monitoring Integration: Collect health and metrics data from all servers.
  • Configuration Helpers: Bulk operations on server configurations with validation.
  • Extensive Testing: 80.4% coverage with race detection and comprehensive test scenarios.

Architecture

Component Diagram
┌────────────────────────────────────────────────────┐
│                        Pool                        │
├────────────────────────────────────────────────────┤
│                                                    │
│  ┌──────────────┐        ┌──────────────────────┐  │
│  │   Context    │        │   Handler Function   │  │
│  │   Provider   │        │   (shared optional)  │  │
│  └──────┬───────┘        └──────────┬───────────┘  │
│         │                           │              │
│         ▼                           ▼              │
│  ┌──────────────────────────────────────────────┐  │
│  │     Server Map (libctx.Config[string])       │  │
│  │     Key: Bind Address (e.g., "0.0.0.0:8080") │  │
│  │     Value: libhtp.Server instance            │  │
│  └──────────────────────────────────────────────┘  │
│         │                                          │
│         ▼                                          │
│  ┌─────────────────────────────────────────────┐   │
│  │  Individual Server Instances                │   │
│  │                                             │   │
│  │  Server 1 ──┐  Server 2 ──┐  Server N ──┐   │   │
│  │  :8080      │  :8443      │  :9000      │   │   │
│  │  HTTP       │  HTTPS      │  Custom     │   │   │
│  └─────────────────────────────────────────────┘   │
│                                                    │
│  ┌─────────────────────────────────────────────┐   │
│  │          Pool Operations                    │   │
│  │  - Walk: Iterate all servers                │   │
│  │  - WalkLimit: Iterate specific servers      │   │
│  │  - Filter: Query by criteria                │   │
│  │  - Start/Stop/Restart: Lifecycle            │   │
│  │  - Monitor: Health and metrics              │   │
│  └─────────────────────────────────────────────┘   │
│                                                    │
└────────────────────────────────────────────────────┘
Data Flow

Server Lifecycle:

  1. Configuration Phase: Servers defined via libhtp.Config
  2. Pool Creation: New() creates empty pool or with initial servers
  3. Server Addition: StoreNew() validates config and adds server to map
  4. Lifecycle Control: Start() initiates all servers concurrently
  5. Runtime Operations: Filter, Walk, Monitor during operation
  6. Graceful Shutdown: Stop() drains and closes all servers

Error Handling:

  1. Validation errors collected during config validation
  2. Startup errors aggregated during Start()
  3. Shutdown errors collected during Stop()
  4. All errors use liberr.Error with proper code hierarchy

Performance

Characteristics

Operation Complexity:

  • Store/Load/Delete: O(1) average, O(n) worst case (map operations)
  • Walk/WalkLimit: O(n) where n is number of servers
  • Filter: O(n) with regex matching overhead
  • List: O(n) + O(m) where m is filtered result size
  • Start/Stop/Restart: O(n) parallel server operations

Memory Usage:

  • Base pool overhead: ~200 bytes
  • Per-server overhead: ~100 bytes (map entry)
  • Total: Base + (n × Server size) + (n × Overhead)
  • Typical pool with 10 servers: ~50KB

Concurrency:

  • Read operations scale with goroutines (RLock)
  • Write operations serialize (Lock)
  • Server lifecycle operations run concurrently
  • No goroutine leaks during normal operation
  • Zero race conditions verified with -race detector
Complexity
Operation Time Space Notes
New O(1) O(1) Constant initialization
StoreNew O(1) O(1) Map insertion
Load O(1) O(1) Map lookup
Delete O(1) O(1) Map deletion
Walk O(n) O(1) Iterate all servers
Filter O(n) O(m) m = result size
Start O(n) O(n) Parallel goroutines
Stop O(n) O(n) Parallel goroutines
Memory Usage
  • Sequential Write: Zero allocations per operation
  • Parallel Write: ~1 allocation per writer (goroutine stack)
  • Struct Overhead: ~1KB base size (atomic values, maps)

Use Cases

Multi-Port HTTP Server

Run HTTP and HTTPS servers simultaneously:

httpCfg := libhtp.Config{
    Name:   "http",
    Listen: ":80",
    Expose: "http://example.com",
}

httpsCfg := libhtp.Config{
    Name:   "https",
    Listen: ":443",
    Expose: "https://example.com",
}

p := pool.New(nil, sharedHandler)
p.StoreNew(httpCfg, nil)
p.StoreNew(httpsCfg, nil)
p.Start(context.Background())
Microservices Gateway

Route different services on different ports:

configs := pool.Config{
    makeConfig("users-api", ":8081"),
    makeConfig("orders-api", ":8082"),
    makeConfig("payments-api", ":8083"),
}

p, _ := configs.Pool(nil, nil, logger)
p.Start(context.Background())
Development vs Production

Different configurations per environment:

var configs pool.Config
if isProd {
    configs = makeTLSConfigs()
} else {
    configs = makeHTTPConfigs()
}

p, _ := configs.Pool(ctx, handler, logger)
Admin and Public Separation

Isolate administrative interfaces:

publicCfg := libhtp.Config{
    Name:   "public",
    Listen: "0.0.0.0:8080",
    Expose: "https://api.example.com",
}

adminCfg := libhtp.Config{
    Name:   "admin",
    Listen: "127.0.0.1:9000", // localhost only
    Expose: "http://localhost:9000",
}

Quick Start

Installation
go get github.com/nabbar/golib/httpserver/pool
Basic Usage
package main

import (
    "context"
    "net/http"
    
    libhtp "github.com/nabbar/golib/httpserver"
    "github.com/nabbar/golib/httpserver/pool"
)

func main() {
    // Create pool
    p := pool.New(nil, nil)
    
    // Configure server
    cfg := libhtp.Config{
        Name:   "api-server",
        Listen: "0.0.0.0:8080",
        Expose: "http://localhost:8080",
    }
    cfg.RegisterHandlerFunc(func() map[string]http.Handler {
        return map[string]http.Handler{
            "/": http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                w.Write([]byte("Hello from pool!"))
            }),
        }
    })
    
    // Add to pool
    p.StoreNew(cfg, nil)
    
    // Start server
    p.Start(context.Background())
    
    // Graceful shutdown
    defer p.Stop(context.Background())
}
Multiple Servers
configs := pool.Config{
    {Name: "api", Listen: ":8080", Expose: "http://localhost:8080"},
    {Name: "admin", Listen: ":9000", Expose: "http://localhost:9000"},
}

configs.SetHandlerFunc(sharedHandler)

p, err := configs.Pool(nil, nil, nil)
if err != nil {
    log.Fatal(err)
}

p.Start(context.Background())
Filtering
// Filter by name pattern
apiServers := p.Filter(srvtps.FieldName, "", "^api-.*")

// Filter by bind address
localServers := p.Filter(srvtps.FieldBind, "", "^127\\.0\\.0\\.1:.*")

// List server names
names := p.List(srvtps.FieldName, srvtps.FieldName, "", ".*")
Lifecycle Management
// Check if servers are running
if p.IsRunning() {
    log.Println("Servers are active")
}

// Get uptime
uptime := p.Uptime()

// Restart all servers
p.Restart(context.Background())

// Get monitoring data
version := libver.Version{}
monitors, _ := p.Monitor(version)

Best Practices

DO:

  • ✅ Validate all configurations before pool creation
  • ✅ Use unique bind addresses for each server
  • ✅ Set appropriate context timeouts for Start/Stop operations
  • ✅ Check error codes for specific failure types
  • ✅ Use Filter operations to manage subsets of servers
  • ✅ Clean up pools with defer Stop(ctx)
  • ✅ Use monitoring integration for production observability

DON'T:

  • ❌ Don't assume all operations succeed (check errors)
  • ❌ Don't use the same bind address for multiple servers
  • ❌ Don't ignore validation errors
  • ❌ Don't block indefinitely on Start/Stop (use context timeouts)
  • ❌ Don't modify server configurations directly (use pool methods)
  • ❌ Don't forget to handle partial failures during batch operations

API Reference

Pool Interface

Lifecycle Management:

  • Start(ctx context.Context) error - Start all servers
  • Stop(ctx context.Context) error - Stop all servers gracefully
  • Restart(ctx context.Context) error - Restart all servers
  • IsRunning() bool - Check if at least one server is running
  • Uptime() time.Duration - Get maximum uptime of all servers

Server Management:

  • Store(srv libhtp.Server) - Add pre-configured server
  • StoreNew(cfg libhtp.Config, def liblog.FuncLog) error - Create and add server
  • Load(bindAddress string) libhtp.Server - Retrieve server by bind address
  • Delete(bindAddress string) - Remove server from pool
  • LoadAndDelete(bindAddress string) (libhtp.Server, bool) - Atomic load and delete
  • Clean() - Remove all servers

Iteration:

  • Walk(fct FuncWalk) - Iterate all servers
  • WalkLimit(fct FuncWalk, bindAddress ...string) - Iterate specific servers

Filtering:

  • Has(bindAddress string) bool - Check if server exists
  • Len() int - Get server count
  • Filter(field srvtps.Field, pattern, regex string) Pool - Create filtered pool
  • List(source, target srvtps.Field, pattern, regex string) []string - Extract field values

Pool Operations:

  • Clone(ctx context.Context) Pool - Create independent copy
  • Merge(p Pool, def liblog.FuncLog) error - Merge another pool
  • Handler(fct srvtps.FuncHandler) - Register shared handler

Monitoring:

  • MonitorNames() []string - Get monitor identifiers
  • Monitor(vrs libver.Version) ([]montps.Monitor, liberr.Error) - Collect monitoring data
Configuration

Config Type:

type Config []libhtp.Config

Methods:

  • SetHandlerFunc(fct srvtps.FuncHandler) - Set handler for all configs
  • SetDefaultTLS(cfg *tls.Config) - Set TLS configuration
  • SetContext(ctx context.Context) - Set context provider
  • Pool(ctx context.Context, hdl srvtps.FuncHandler, log liblog.FuncLog) (Pool, liberr.Error) - Create pool
  • Walk(fct FuncWalkConfig) - Iterate configurations
  • Validate() liberr.Error - Validate all configurations
Filtering

Field Types:

  • FieldName - Server name
  • FieldBind - Bind address (e.g., "127.0.0.1:8080")
  • FieldExpose - Expose address (e.g., "http://localhost:8080")

Pattern Matching:

  • Exact match: Use pattern parameter
  • Regex match: Use regex parameter
  • Case-insensitive for exact matches
  • Go regex syntax for regex matches
Error Codes
  • ErrorParamEmpty - Invalid or empty parameters
  • ErrorPoolAdd - Failed to add server to pool
  • ErrorPoolValidate - Configuration validation failure
  • ErrorPoolStart - One or more servers failed to start
  • ErrorPoolStop - One or more servers failed to stop
  • ErrorPoolRestart - One or more servers failed to restart
  • ErrorPoolMonitor - Monitoring operation failure

Contributing

Contributions are welcome! Please ensure:

  • ✅ All tests pass with race detector enabled
  • ✅ Code coverage remains ≥80%
  • ✅ GoDoc comments for all exported functions
  • ✅ Follow existing code style and patterns
  • ✅ Add tests for new features

See TESTING.md for detailed test documentation.


Improvements & Security

Current Status

The pool package is production-ready with:

  • Thread-safe concurrent operations
  • 80.4% test coverage with comprehensive scenarios
  • Zero race conditions verified with -race detector
  • Error handling with liberr.Error hierarchy
Future Enhancements (Non-urgent)

The following enhancements could be considered for future versions:

  1. Automatic Port Allocation: Dynamic port assignment for servers
  2. Health Checks: Automatic server health monitoring and restart
  3. Load Balancing: Built-in traffic distribution between servers
  4. Configuration Reload: Hot-reload of server configurations
  5. Metrics Export: Optional integration with Prometheus

These are optional improvements and not required for production use. The current implementation is stable and performant.


Resources

Package Documentation
  • GoDoc - Complete API reference with function signatures, method descriptions, and runnable examples. Essential for understanding the public interface and usage patterns.

  • doc.go - In-depth package documentation including design philosophy, architecture diagrams, thread-safety guarantees, and implementation details. Provides detailed explanations of internal mechanisms and best practices for production use.

  • TESTING.md - Comprehensive test suite documentation covering test architecture, BDD methodology with Ginkgo v2, 80.4% coverage analysis, and guidelines for writing new tests. Includes troubleshooting and test inventory.

External References
  • net/http - Go standard library HTTP package. The pool manages multiple http.Server instances with unified control.

  • Effective Go - Official Go programming guide covering best practices for interfaces, error handling, and concurrency patterns.


AI Transparency

In compliance with EU AI Act Article 50.4: AI assistance was used for testing, documentation, and bug resolution under human supervision. All core functionality is human-designed and validated.


License

MIT License - See LICENSE file for details.

Copyright (c) 2025 Nicolas JUHEL


Maintained by: Nicolas JUHEL Package: github.com/nabbar/golib/httpserver/pool Version: See releases for versioning

Documentation

Overview

Package pool provides unified management of multiple HTTP servers through a thread-safe pool abstraction. It enables simultaneous operation of multiple servers with different configurations, unified lifecycle control, and advanced filtering capabilities.

Overview

The pool package extends github.com/nabbar/golib/httpserver by providing a container for managing multiple HTTP server instances as a cohesive unit. All servers in the pool can be started, stopped, or restarted together while maintaining individual configurations and bind addresses.

Key capabilities:

  • Unified lifecycle management (Start/Stop/Restart all servers)
  • Thread-safe concurrent operations with sync.RWMutex
  • Advanced filtering by name, bind address, or expose address
  • Server merging and configuration validation
  • Monitoring integration for all pooled servers
  • Dynamic server addition and removal during operation

Design Philosophy

1. Unified Management: Control multiple heterogeneous servers as a single logical unit 2. Thread Safety First: All operations are protected by mutex for concurrent safety 3. Flexibility: Support for dynamic server addition, removal, and configuration updates 4. Observability: Built-in monitoring and health check integration 5. Error Aggregation: Collect and report errors from all servers systematically

Architecture

The pool implementation uses a layered architecture:

┌─────────────────────────────────────────────────────────┐
│                         Pool                            │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌──────────────┐        ┌──────────────────────┐       │
│  │   Context    │        │   Handler Function   │       │
│  │   Provider   │        │   (shared optional)  │       │
│  └──────┬───────┘        └──────────┬───────────┘       │
│         │                           │                   │
│         ▼                           ▼                   │
│  ┌──────────────────────────────────────────────┐       │
│  │     Server Map (libctx.Config[string])       │       │
│  │     Key: Bind Address (e.g., "0.0.0.0:8080") │       │
│  │     Value: libhtp.Server instance            │       │
│  └──────────────────────────────────────────────┘       │
│         │                                               │
│         ▼                                               │
│  ┌─────────────────────────────────────────────┐        │
│  │  Individual Server Instances                │        │
│  │                                             │        │
│  │  Server 1 ──┐  Server 2 ──┐  Server N ──┐   │        │
│  │  :8080      │  :8443      │  :9000      │   │        │
│  │  HTTP       │  HTTPS      │  Custom     │   │        │
│  └─────────────────────────────────────────────┘        │
│                                                         │
│  ┌─────────────────────────────────────────────┐        │
│  │          Pool Operations                    │        │
│  │  - Walk: Iterate all servers                │        │
│  │  - WalkLimit: Iterate specific servers      │        │
│  │  - Filter: Query by criteria                │        │
│  │  - Start/Stop/Restart: Lifecycle            │        │
│  │  - Monitor: Health and metrics              │        │
│  └─────────────────────────────────────────────┘        │
│                                                         │
└─────────────────────────────────────────────────────────┘

Data Flow

Server Lifecycle:

  1. Configuration Phase: Servers defined via libhtp.Config
  2. Pool Creation: New() creates empty pool or with initial servers
  3. Server Addition: StoreNew() validates config and adds server
  4. Lifecycle Control: Start() initiates all servers concurrently
  5. Runtime Operations: Filter, Walk, Monitor during operation
  6. Graceful Shutdown: Stop() drains and closes all servers

Error Handling:

  1. Validation errors collected during config validation
  2. Startup errors aggregated during Start()
  3. Shutdown errors collected during Stop()
  4. All errors use liberr.Error with proper code hierarchy

Thread Safety

All pool operations are thread-safe through sync.RWMutex:

  • Read operations (Load, Walk, Filter, List) use RLock
  • Write operations (Store, Delete, Clean) use Lock
  • Atomic server map updates via libctx.Config
  • Safe concurrent access to individual servers

Synchronization guarantees:

  • No data races during concurrent operations
  • Consistent view of server collection
  • Safe iteration during modifications
  • Proper memory barriers for visibility

Basic Usage

Creating and managing a simple pool:

// Create configurations
cfg1 := libhtp.Config{
    Name:   "api-server",
    Listen: "0.0.0.0:8080",
    Expose: "http://api.example.com",
}
cfg1.RegisterHandlerFunc(apiHandler)

cfg2 := libhtp.Config{
    Name:   "admin-server",
    Listen: "127.0.0.1:9000",
    Expose: "http://localhost:9000",
}
cfg2.RegisterHandlerFunc(adminHandler)

// Create pool and add servers
p := pool.New(nil, nil)
p.StoreNew(cfg1, nil)
p.StoreNew(cfg2, nil)

// Start all servers
ctx := context.Background()
if err := p.Start(ctx); err != nil {
    log.Fatal(err)
}

// Graceful shutdown
defer func() {
    stopCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    p.Stop(stopCtx)
}()

Advanced Features

## Configuration Helpers

The Config slice type provides bulk operations:

configs := pool.Config{cfg1, cfg2, cfg3}

// Validate all configurations
if err := configs.Validate(); err != nil {
    log.Fatal(err)
}

// Set shared handler for all
configs.SetHandlerFunc(sharedHandler)

// Set shared context
configs.SetContext(context.Background())

// Create pool from configs
p, err := configs.Pool(nil, nil, nil)

## Dynamic Server Management

Add, remove, and update servers during operation:

// Add new server dynamically
newCfg := libhtp.Config{Name: "metrics", Listen: ":2112"}
p.StoreNew(newCfg, nil)

// Remove server
p.Delete(":2112")

// Atomic load and delete
if srv, ok := p.LoadAndDelete(":8080"); ok {
    srv.Stop(ctx)
}

// Clear all servers
p.Clean()

## Filtering and Querying

Filter servers by various criteria:

// Filter by name pattern
apiServers := p.Filter(srvtps.FieldName, "", "^api-.*")

// Filter by bind address
localServers := p.Filter(srvtps.FieldBind, "", "^127\\.0\\.0\\.1:.*")

// Filter by expose address
prodServers := p.Filter(srvtps.FieldExpose, "", ".*\\.example\\.com.*")

// List specific fields
names := p.List(srvtps.FieldBind, srvtps.FieldName, "", ".*")

// Chain filters
filtered := p.Filter(srvtps.FieldBind, "", "^0\\.0\\.0\\.0:.*").
              Filter(srvtps.FieldName, "", "^api-.*")

## Pool Merging

Combine multiple pools:

pool1 := New(nil, nil)
pool1.StoreNew(cfg1, nil)

pool2 := New(nil, nil)
pool2.StoreNew(cfg2, nil)

// Merge pool2 into pool1
if err := pool1.Merge(pool2, nil); err != nil {
    log.Printf("Merge error: %v", err)
}

## Iteration and Inspection

Walk through servers with custom logic:

// Iterate all servers
p.Walk(func(bindAddress string, srv libhtp.Server) bool {
    log.Printf("Server %s at %s", srv.GetName(), bindAddress)
    return true // continue iteration
})

// Iterate specific servers
p.WalkLimit(func(bindAddress string, srv libhtp.Server) bool {
    if srv.IsRunning() {
        log.Printf("Running: %s", srv.GetName())
    }
    return true
}, ":8080", ":8443")

// Check server existence
if p.Has(":8080") {
    log.Println("Server on :8080 exists")
}

// Get server count
log.Printf("Pool has %d servers", p.Len())

## Monitoring Integration

Access monitoring data for all servers:

version := libver.New("MyApp", "1.0.0")
monitors, err := p.Monitor(version)
if err != nil {
    log.Printf("Monitor errors: %v", err)
}

for _, mon := range monitors {
    log.Printf("Server %s status: %v", mon.Name, mon.Health)
}

// Get monitor identifiers
names := p.MonitorNames()

Use Cases

## Multi-Port HTTP Server

Run HTTP and HTTPS servers simultaneously:

httpCfg := libhtp.Config{
    Name:   "http",
    Listen: ":80",
    Expose: "http://example.com",
}

httpsCfg := libhtp.Config{
    Name:   "https",
    Listen: ":443",
    Expose: "https://example.com",
    // TLS configuration...
}

p := pool.New(nil, sharedHandler)
p.StoreNew(httpCfg, nil)
p.StoreNew(httpsCfg, nil)
p.Start(context.Background())

## Microservices Gateway

Route different services on different ports:

configs := pool.Config{
    makeConfig("users-api", ":8081", usersHandler),
    makeConfig("orders-api", ":8082", ordersHandler),
    makeConfig("payments-api", ":8083", paymentsHandler),
}

p, err := configs.Pool(nil, nil, logger)
p.Start(context.Background())

## Development vs Production

Different configurations per environment:

var configs pool.Config
if isProd {
    configs = pool.Config{
        makeTLSConfig("api", ":443"),
        makeTLSConfig("admin", ":8443"),
    }
} else {
    configs = pool.Config{
        makeConfig("api", ":8080"),
        makeConfig("admin", ":9000"),
    }
}

p, _ := configs.Pool(ctx, handler, logger)

## Blue-Green Deployment

Gradually switch traffic between server pools:

bluePool := createPoolFromConfigs(blueConfigs)
greenPool := createPoolFromConfigs(greenConfigs)

// Start green pool
greenPool.Start(ctx)

// Switch traffic...
time.Sleep(verificationPeriod)

// Shutdown blue pool
bluePool.Stop(ctx)

## Admin and Public Separation

Isolate administrative interfaces:

publicCfg := libhtp.Config{
    Name:   "public",
    Listen: "0.0.0.0:8080",
    Expose: "https://api.example.com",
}

adminCfg := libhtp.Config{
    Name:   "admin",
    Listen: "127.0.0.1:9000", // localhost only
    Expose: "http://localhost:9000",
}

p := pool.New(nil, nil)
p.StoreNew(publicCfg, nil)
p.StoreNew(adminCfg, nil)

Performance Characteristics

Pool operations have the following complexity:

  • Store/Load/Delete: O(1) average, O(n) worst case (map operations)
  • Walk/WalkLimit: O(n) where n is number of servers
  • Filter: O(n) with regex matching overhead
  • List: O(n) + O(m) where m is filtered result size
  • Start/Stop/Restart: O(n) parallel server operations

Memory usage:

  • Base pool overhead: ~200 bytes
  • Per-server overhead: ~100 bytes (map entry)
  • Total: Base + (n × Server size) + (n × Overhead)
  • Typical pool with 10 servers: ~50KB

Concurrency:

  • Read operations scale with goroutines (RLock)
  • Write operations serialize (Lock)
  • Server lifecycle operations run concurrently
  • No goroutine leaks during normal operation

Error Handling

The package defines error codes in the liberr hierarchy:

  • ErrorParamEmpty: Invalid or empty parameters
  • ErrorPoolAdd: Failed to add server to pool
  • ErrorPoolValidate: Configuration validation failure
  • ErrorPoolStart: One or more servers failed to start
  • ErrorPoolStop: One or more servers failed to stop
  • ErrorPoolRestart: One or more servers failed to restart
  • ErrorPoolMonitor: Monitoring operation failure

All errors implement liberr.Error interface with:

  • Error code for programmatic handling
  • Parent error chains for context
  • Multiple error aggregation support

Limitations and Constraints

Known limitations:

  1. Bind Address Uniqueness: Each server must have a unique bind address. Attempting to add servers with duplicate bind addresses will overwrite the existing server.

  2. No Automatic Port Allocation: The pool does not assign ports automatically. All bind addresses must be explicitly configured.

  3. Synchronous Lifecycle: Start/Stop/Restart operations are synchronous and wait for all servers to complete. Use context timeouts for control.

  4. No Load Balancing: The pool manages servers but does not distribute traffic between them. Use external load balancers for traffic distribution.

  5. Error Aggregation: Errors from multiple servers are collected but the first error stops iteration in some operations (e.g., Merge).

  6. No Health Checks: The pool does not perform automatic health checks. Integrate with monitoring systems for health management.

Best Practices

DO:

  • Validate all configurations before pool creation
  • Use unique bind addresses for each server
  • Set appropriate context timeouts for Start/Stop operations
  • Check error codes for specific failure types
  • Use Filter operations to manage subsets of servers
  • Clean up pools with defer Stop(ctx)
  • Use monitoring integration for production observability

DON'T:

  • Don't assume all operations succeed (check errors)
  • Don't use the same bind address for multiple servers
  • Don't ignore validation errors
  • Don't block indefinitely on Start/Stop (use context timeouts)
  • Don't modify server configurations directly (use pool methods)
  • Don't forget to handle partial failures during batch operations

Integration with golib Ecosystem

The pool package integrates with:

  • github.com/nabbar/golib/httpserver: Server implementation
  • github.com/nabbar/golib/httpserver/types: Server types and interfaces
  • github.com/nabbar/golib/context: Context management utilities
  • github.com/nabbar/golib/logger: Logging integration
  • github.com/nabbar/golib/errors: Error handling framework
  • github.com/nabbar/golib/monitor/types: Monitoring abstractions
  • github.com/nabbar/golib/runner: Lifecycle management interfaces

Testing

The package includes comprehensive tests:

  • Pool creation and initialization tests
  • Server management operation tests (Store/Load/Delete)
  • Filtering and querying tests
  • Merge and clone operation tests
  • Configuration validation tests
  • Lifecycle management tests
  • Error handling and edge case tests

Run tests with:

go test -v ./httpserver/pool
CGO_ENABLED=1 go test -race -v ./httpserver/pool

Thread Safety Validation

All operations are validated for thread safety:

  • Zero race conditions with -race detector
  • Concurrent read/write test scenarios
  • Stress tests with multiple goroutines
  • Safe server addition during iteration

For single server management, see:

  • github.com/nabbar/golib/httpserver: Individual HTTP server
  • github.com/nabbar/golib/httpserver/types: Server type definitions

For other pooling patterns, see:

  • github.com/nabbar/golib/runner/startStop: Generic runner pool
Example (Clean)

Example_clean demonstrates clearing all servers. Shows how to empty the pool.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)

	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	cfg1 := libhtp.Config{Name: "s1", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"}
	cfg1.RegisterHandlerFunc(handler)
	p.StoreNew(cfg1, nil)

	cfg2 := libhtp.Config{Name: "s2", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"}
	cfg2.RegisterHandlerFunc(handler)
	p.StoreNew(cfg2, nil)

	fmt.Printf("Before clean: %d servers\n", p.Len())

	p.Clean()
	fmt.Printf("After clean: %d servers\n", p.Len())

}
Output:

Before clean: 2 servers
After clean: 0 servers
Example (Clone)

Example_clone demonstrates cloning a pool. Shows how to create an independent copy of a pool.

package main

import (
	"context"
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	original := pool.New(nil, nil)

	cfg := libhtp.Config{
		Name:   "original-server",
		Listen: "127.0.0.1:8080",
		Expose: "http://localhost:8080",
	}
	cfg.RegisterHandlerFunc(func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	})

	original.StoreNew(cfg, nil)

	cloned := original.Clone(context.Background())

	fmt.Printf("Original: %d servers\n", original.Len())
	fmt.Printf("Cloned: %d servers\n", cloned.Len())

}
Output:

Original: 1 servers
Cloned: 1 servers
Example (ComplexFiltering)

Example_complexFiltering demonstrates chaining multiple filters. Shows advanced filtering techniques.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"

	srvtps "github.com/nabbar/golib/httpserver/types"
)

func main() {
	p := pool.New(nil, nil)

	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	configs := []libhtp.Config{
		{Name: "api-public", Listen: "0.0.0.0:8080", Expose: "http://api.example.com:8080"},
		{Name: "api-private", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
		{Name: "web-public", Listen: "0.0.0.0:80", Expose: "http://www.example.com"},
	}

	for _, cfg := range configs {
		cfg.RegisterHandlerFunc(handler)
		p.StoreNew(cfg, nil)
	}

	filtered := p.Filter(srvtps.FieldBind, "", "^0\\.0\\.0\\.0:.*").
		Filter(srvtps.FieldName, "", "^api-.*")

	fmt.Printf("Public API servers: %d\n", filtered.Len())
}
Output:

Public API servers: 1
Example (ConfigSlice)

Example_configSlice demonstrates using Config slice for bulk operations. Shows validation and pool creation from multiple configurations.

package main

import (
	"fmt"
	"net/http"

	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	configs := pool.Config{
		{Name: "server1", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
		{Name: "server2", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"},
	}

	configs.SetHandlerFunc(handler)

	err := configs.Validate()
	if err != nil {
		fmt.Printf("Validation error: %v\n", err)
		return
	}

	p, err := configs.Pool(nil, nil, nil)
	if err != nil {
		fmt.Printf("Pool creation error: %v\n", err)
		return
	}

	fmt.Printf("Created pool with %d servers\n", p.Len())
}
Output:

Created pool with 2 servers
Example (ConfigWalk)

Example_configWalk demonstrates walking through configuration slice. Shows how to iterate over configurations before pool creation.

package main

import (
	"fmt"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	configs := pool.Config{
		{Name: "config1", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
		{Name: "config2", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"},
	}

	var count int
	configs.Walk(func(cfg libhtp.Config) bool {
		count++
		fmt.Printf("Config: %s\n", cfg.Name)
		return true
	})

	fmt.Printf("Total: %d configs\n", count)

}
Output:

Config: config1
Config: config2
Total: 2 configs
Example (Delete)

Example_delete demonstrates removing servers from the pool. Shows server deletion operations.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)

	cfg := libhtp.Config{
		Name:   "temp-server",
		Listen: "127.0.0.1:8080",
		Expose: "http://localhost:8080",
	}
	cfg.RegisterHandlerFunc(func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	})

	p.StoreNew(cfg, nil)
	fmt.Printf("Before delete: %d servers\n", p.Len())

	p.Delete("127.0.0.1:8080")
	fmt.Printf("After delete: %d servers\n", p.Len())

}
Output:

Before delete: 1 servers
After delete: 0 servers
Example (Filter)

Example_filter demonstrates filtering servers by criteria. Shows how to create filtered subsets of the pool.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"

	srvtps "github.com/nabbar/golib/httpserver/types"
)

func main() {
	p := pool.New(nil, nil)

	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	configs := []libhtp.Config{
		{Name: "api-server", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
		{Name: "api-v2-server", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"},
		{Name: "web-server", Listen: "127.0.0.1:9000", Expose: "http://localhost:9000"},
	}

	for _, cfg := range configs {
		cfg.RegisterHandlerFunc(handler)
		p.StoreNew(cfg, nil)
	}

	filtered := p.Filter(srvtps.FieldName, "", "^api-.*")

	fmt.Printf("Filtered pool has %d servers\n", filtered.Len())
}
Output:

Filtered pool has 2 servers
Example (HandlerUpdate)

Example_handlerUpdate demonstrates updating handler function. Shows how to change the shared handler function.

package main

import (
	"fmt"
	"net/http"

	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)

	handler := func() map[string]http.Handler {
		return map[string]http.Handler{
			"/api": http.NotFoundHandler(),
		}
	}

	p.Handler(handler)

	fmt.Println("Handler updated successfully")
}
Output:

Handler updated successfully
Example (List)

Example_list demonstrates listing server attributes. Shows how to extract specific fields from servers.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"

	srvtps "github.com/nabbar/golib/httpserver/types"
)

func main() {
	p := pool.New(nil, nil)

	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	configs := []libhtp.Config{
		{Name: "server1", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
		{Name: "server2", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"},
	}

	for _, cfg := range configs {
		cfg.RegisterHandlerFunc(handler)
		p.StoreNew(cfg, nil)
	}

	names := p.List(srvtps.FieldName, srvtps.FieldName, "", ".*")

	fmt.Printf("Found %d servers\n", len(names))

}
Output:

Found 2 servers
Example (LoadAndCheck)

Example_loadAndCheck demonstrates loading servers and checking existence. Shows how to retrieve and verify servers in the pool.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)

	cfg := libhtp.Config{
		Name:   "test-server",
		Listen: "127.0.0.1:8080",
		Expose: "http://localhost:8080",
	}
	cfg.RegisterHandlerFunc(func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	})

	p.StoreNew(cfg, nil)

	if p.Has("127.0.0.1:8080") {
		srv := p.Load("127.0.0.1:8080")
		fmt.Printf("Found server: %s\n", srv.GetName())
	}

	if !p.Has("127.0.0.1:9999") {
		fmt.Println("Server on :9999 not found")
	}

}
Output:

Found server: test-server
Server on :9999 not found
Example (LoadAndDelete)

Example_loadAndDelete demonstrates atomic load and delete. Shows how to retrieve and remove a server in one operation.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)

	cfg := libhtp.Config{
		Name:   "remove-me",
		Listen: "127.0.0.1:8080",
		Expose: "http://localhost:8080",
	}
	cfg.RegisterHandlerFunc(func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	})

	p.StoreNew(cfg, nil)

	srv, loaded := p.LoadAndDelete("127.0.0.1:8080")
	if loaded {
		fmt.Printf("Removed: %s\n", srv.GetName())
	}

	fmt.Printf("Pool now has %d servers\n", p.Len())

}
Output:

Removed: remove-me
Pool now has 0 servers
Example (Merge)

Example_merge demonstrates merging two pools. Shows how to combine servers from different pools.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	pool1 := pool.New(nil, nil)
	pool2 := pool.New(nil, nil)

	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	cfg1 := libhtp.Config{Name: "server1", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"}
	cfg1.RegisterHandlerFunc(handler)
	pool1.StoreNew(cfg1, nil)

	cfg2 := libhtp.Config{Name: "server2", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"}
	cfg2.RegisterHandlerFunc(handler)
	pool2.StoreNew(cfg2, nil)

	err := pool1.Merge(pool2, nil)
	if err != nil {
		fmt.Printf("Merge error: %v\n", err)
		return
	}

	fmt.Printf("Merged pool has %d servers\n", pool1.Len())
}
Output:

Merged pool has 2 servers
Example (MonitorNames)

Example_monitorNames demonstrates accessing monitoring identifiers. Shows how to retrieve monitor names for all servers.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)

	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	configs := []libhtp.Config{
		{Name: "api", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
		{Name: "web", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"},
	}

	for _, cfg := range configs {
		cfg.RegisterHandlerFunc(handler)
		p.StoreNew(cfg, nil)
	}

	names := p.MonitorNames()
	fmt.Printf("Monitor count: %d\n", len(names))

}
Output:

Monitor count: 2
Example (MultiStepPool)

Example_multiStepPool demonstrates a complete pool lifecycle. Shows configuration, creation, management, and cleanup.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	configs := pool.Config{
		{Name: "primary", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
		{Name: "backup", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"},
	}

	configs.SetHandlerFunc(handler)

	if err := configs.Validate(); err != nil {
		fmt.Printf("Validation failed: %v\n", err)
		return
	}

	p, err := configs.Pool(nil, nil, nil)
	if err != nil {
		fmt.Printf("Pool creation failed: %v\n", err)
		return
	}

	fmt.Printf("Phase 1: %d servers\n", p.Len())

	newCfg := libhtp.Config{
		Name:   "emergency",
		Listen: "127.0.0.1:9000",
		Expose: "http://localhost:9000",
	}
	newCfg.RegisterHandlerFunc(handler)
	p.StoreNew(newCfg, nil)

	fmt.Printf("Phase 2: %d servers\n", p.Len())

	p.Delete("127.0.0.1:8081")

	fmt.Printf("Phase 3: %d servers\n", p.Len())

}
Output:

Phase 1: 2 servers
Phase 2: 3 servers
Phase 3: 2 servers
Example (MultipleServers)

Example_multipleServers demonstrates managing multiple servers in a pool. Shows how to add several servers with different configurations.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)

	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	configs := []libhtp.Config{
		{Name: "api", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
		{Name: "admin", Listen: "127.0.0.1:9000", Expose: "http://localhost:9000"},
		{Name: "metrics", Listen: "127.0.0.1:2112", Expose: "http://localhost:2112"},
	}

	for _, cfg := range configs {
		cfg.RegisterHandlerFunc(handler)
		p.StoreNew(cfg, nil)
	}

	fmt.Printf("Pool contains %d servers\n", p.Len())
}
Output:

Pool contains 3 servers
Example (SimplePool)

Example_simplePool demonstrates creating a pool with a single server. This shows basic server configuration and addition to the pool.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)

	cfg := libhtp.Config{
		Name:   "simple-server",
		Listen: "127.0.0.1:8080",
		Expose: "http://localhost:8080",
	}
	cfg.RegisterHandlerFunc(func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	})

	err := p.StoreNew(cfg, nil)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Pool has %d server(s)\n", p.Len())
}
Output:

Pool has 1 server(s)
Example (Walk)

Example_walk demonstrates iterating over all servers in the pool. Shows how to execute logic for each server.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)

	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	configs := []libhtp.Config{
		{Name: "server-a", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
		{Name: "server-b", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"},
	}

	for _, cfg := range configs {
		cfg.RegisterHandlerFunc(handler)
		p.StoreNew(cfg, nil)
	}

	var count int
	p.Walk(func(bindAddress string, srv libhtp.Server) bool {
		count++
		return true
	})

	fmt.Printf("Walked through %d servers\n", count)

}
Output:

Walked through 2 servers
Example (WalkLimit)

Example_walkLimit demonstrates iterating over specific servers. Shows how to process only selected servers by bind address.

package main

import (
	"fmt"
	"net/http"

	libhtp "github.com/nabbar/golib/httpserver"
	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)

	handler := func() map[string]http.Handler {
		return map[string]http.Handler{"": http.NotFoundHandler()}
	}

	configs := []libhtp.Config{
		{Name: "api", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
		{Name: "web", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"},
		{Name: "admin", Listen: "127.0.0.1:9000", Expose: "http://localhost:9000"},
	}

	for _, cfg := range configs {
		cfg.RegisterHandlerFunc(handler)
		p.StoreNew(cfg, nil)
	}

	var names []string
	p.WalkLimit(func(bindAddress string, srv libhtp.Server) bool {
		names = append(names, srv.GetName())
		return true
	}, "127.0.0.1:8080", "127.0.0.1:9000")

	if len(names) == 2 {
		fmt.Printf("Selected %d servers\n", len(names))
	}

}
Output:

Selected 2 servers

Index

Examples

Constants

View Source
const (
	// ErrorParamEmpty indicates that required parameters are missing or empty.
	ErrorParamEmpty liberr.CodeError = iota + liberr.MinPkgHttpServerPool

	// ErrorPoolAdd indicates failure to add a server to the pool.
	ErrorPoolAdd

	// ErrorPoolValidate indicates at least one server configuration is invalid.
	ErrorPoolValidate

	// ErrorPoolStart indicates at least one server failed to start.
	ErrorPoolStart

	// ErrorPoolStop indicates at least one server failed to stop gracefully.
	ErrorPoolStop

	// ErrorPoolRestart indicates at least one server failed to restart.
	ErrorPoolRestart

	// ErrorPoolMonitor indicates failure in monitoring operations.
	ErrorPoolMonitor
)

Error codes for pool operations following liberr.CodeError hierarchy.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config []libhtp.Config

Config is a slice of server configurations used to create a pool of servers. It provides convenience methods for bulk operations on multiple server configurations.

func (Config) Pool

func (p Config) Pool(ctx context.Context, hdl srvtps.FuncHandler, defLog liblog.FuncLog) (Pool, error)

Pool creates a new server pool from the configurations. All configurations are validated and instantiated as servers in the pool. Returns an error if any configuration is invalid or server creation fails.

Parameters:

  • ctx: Context provider for server operations (can be nil)
  • hdl: Handler function for all servers (can be nil if already set on configs)
  • defLog: Default logger function (can be nil)

Returns:

  • Pool: Initialized pool with all servers
  • error: Aggregated errors from server creation, nil if all succeed

func (Config) SetContext

func (p Config) SetContext(f context.Context)

SetContext sets the context provider function for all server configurations. This provides a shared context source for all servers in the configuration.

func (Config) SetDefaultTLS

func (p Config) SetDefaultTLS(f libtls.FctTLSDefault)

SetDefaultTLS sets the default TLS configuration provider for all server configurations. This allows servers to inherit a shared TLS configuration when needed.

func (Config) SetHandlerFunc

func (p Config) SetHandlerFunc(hdl srvtps.FuncHandler)

SetHandlerFunc registers the same handler function with all server configurations in the slice. This is useful for setting a shared handler across multiple servers before pool creation.

func (Config) Validate

func (p Config) Validate() error

Validate validates all server configurations in the slice. Returns an aggregated error containing all validation failures, or nil if all are valid.

Returns:

  • error: Aggregated validation errors, nil if all configurations are valid

func (Config) Walk

func (p Config) Walk(fct FuncWalkConfig)

Walk iterates over all configurations, calling the provided function for each. Iteration stops if the callback returns false or all configurations have been processed. Does nothing if the callback function is nil.

type Filter

type Filter interface {
	// Has checks if a server with the given bind address exists in the pool.
	Has(bindAddress string) bool

	// Len returns the number of servers in the pool.
	Len() int

	// List returns a list of server field values matching the filter criteria.
	// fieldFilter specifies which field to match against, fieldReturn specifies which field to return.
	// Pattern uses glob-style matching (* wildcards), regex uses regular expressions.
	List(fieldFilter, fieldReturn srvtps.FieldType, pattern, regex string) []string

	// Filter creates a new pool containing only servers matching the criteria.
	// field specifies which field to filter on, pattern uses globs, regex uses regular expressions.
	Filter(field srvtps.FieldType, pattern, regex string) Pool
}

Filter provides filtering and querying operations for servers in the pool.

type FuncWalk

type FuncWalk func(bindAddress string, srv libhtp.Server) bool

FuncWalk is a callback function used when iterating over servers in the pool. The function receives the bind address and server instance for each iteration. Return true to continue iteration, false to stop.

type FuncWalkConfig

type FuncWalkConfig func(cfg libhtp.Config) bool

FuncWalkConfig is a callback function for iterating over server configurations. Return true to continue iteration, false to stop.

type Manage

type Manage interface {
	// Walk iterates over all servers in the pool, calling the provided function for each.
	// Iteration stops if the callback returns false. Returns true if all servers were visited.
	Walk(fct FuncWalk)

	// WalkLimit iterates over specific servers identified by their bind addresses.
	// If no addresses are provided, behaves like Walk. Returns true if iteration completed.
	WalkLimit(fct FuncWalk, onlyBindAddress ...string)

	// Clean removes all servers from the pool.
	Clean()

	// Load retrieves a server by its bind address. Returns nil if not found.
	Load(bindAddress string) libhtp.Server

	// Store adds or updates a server in the pool, using its bind address as the key.
	Store(srv libhtp.Server)

	// Delete removes a server from the pool by its bind address.
	Delete(bindAddress string)

	// StoreNew creates a new server from configuration and adds it to the pool.
	// Returns an error if server creation or validation fails.
	StoreNew(cfg libhtp.Config, defLog liblog.FuncLog) error

	// LoadAndDelete atomically retrieves and removes a server.
	// Returns the server and true if found, nil and false otherwise.
	LoadAndDelete(bindAddress string) (val libhtp.Server, loaded bool)

	// MonitorNames returns a list of all monitoring identifiers for servers in the pool.
	MonitorNames() []string
}

Manage provides server management operations for a pool. All operations are thread-safe and can be called concurrently.

type Pool

type Pool interface {
	// Runner embeds base server interface for lifecycle management
	libsrv.Runner

	// Manage embeds server management operations
	Manage

	// Filter embeds filtering and query operations
	Filter

	// Clone creates a deep copy of the pool with an optional new context.
	// The cloned pool contains independent copies of all servers.
	Clone(ctx context.Context) Pool

	// Merge combines servers from another pool into this one.
	// Servers with conflicting bind addresses will be updated.
	Merge(p Pool, def liblog.FuncLog) error

	// Handler registers a handler function for all servers in the pool.
	Handler(fct srvtps.FuncHandler)

	// Monitor retrieves monitoring data for all servers in the pool.
	// Returns a slice of Monitor instances, one per server.
	Monitor(vrs libver.Version) ([]montps.Monitor, liberr.Error)
}

Pool represents a collection of HTTP servers managed as a unified group. It combines server lifecycle management (Start/Stop/Restart) with advanced filtering, monitoring, and configuration operations. All methods are thread-safe.

func New

func New(ctx context.Context, hdl srvtps.FuncHandler, srv ...libhtp.Server) Pool

New creates a new server pool with optional initial servers. The pool manages server lifecycle and provides unified operations across all servers.

Parameters:

  • ctx: Context provider function for server operations (can be nil)
  • hdl: Handler function to register with all servers (can be nil)
  • srv: Optional initial servers to add to the pool

Returns:

  • Pool: Initialized pool ready for use

Example:

pool := pool.New(nil, handlerFunc)
pool.StoreNew(config1, nil)
pool.StoreNew(config2, nil)
pool.Start(context.Background())
Example

ExampleNew demonstrates creating an empty pool. This is the simplest usage pattern - creating a pool container.

package main

import (
	"fmt"

	"github.com/nabbar/golib/httpserver/pool"
)

func main() {
	p := pool.New(nil, nil)
	fmt.Printf("Pool created with %d servers\n", p.Len())
}
Output:

Pool created with 0 servers

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL