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: 18 Imported by: 0

README

Pool Package

Monitor pool management for coordinating multiple health monitors with batch operations and metrics aggregation.

AI Disclaimer: AI tools are used solely to assist with testing, documentation, and bug fixes under human supervision, in compliance with EU AI Act Article 50.4.


Overview

The pool package provides management capabilities for coordinating multiple monitors as a group. It enables batch lifecycle operations, aggregated metrics collection, Prometheus integration, and operational control through shell commands.

Key Features
  • Monitor Management: Add, remove, update, and query monitors by name
  • Batch Operations: Start, stop, and restart all monitors as a unit
  • Metrics Aggregation: Collect and export metrics from all monitors
  • Prometheus Integration: Built-in support for metrics registration and collection
  • Shell Commands: CLI-style commands for operational control (list, info, start, stop, restart, status)
  • Thread-Safe: All operations are safe for concurrent access

Installation

go get github.com/nabbar/golib/monitor/pool

Quick Start

import (
    "context"
    "time"
    "github.com/nabbar/golib/monitor/pool"
)

// Create pool
ctx := context.Background()
p := pool.New(func() context.Context { return ctx })

// Add monitors
p.MonitorAdd(createDatabaseMonitor())
p.MonitorAdd(createAPIMonitor())
p.MonitorAdd(createCacheMonitor())

// Register Prometheus metrics
err := p.RegisterMetrics(prometheusFunc, loggerFunc)
if err != nil {
    panic(err)
}
defer p.UnregisterMetrics()

// Start all monitors
if err := p.Start(ctx); err != nil {
    panic(err)
}
defer p.Stop(ctx)

// Trigger periodic metrics collection
go p.TriggerCollectMetrics(ctx, 30*time.Second)

// Query individual monitor
mon := p.MonitorGet("database")
fmt.Printf("DB Status: %s\n", mon.Status())

// List all monitors
names := p.MonitorList()
fmt.Printf("Monitors: %v\n", names)

Architecture

Component Structure
pool/
├── interface.go     # Pool interface definition
├── model.go         # Pool state management
├── pool.go          # Monitor CRUD operations
├── server.go        # Lifecycle (start/stop/restart)
├── metrics.go       # Prometheus metrics integration
├── shell.go         # Shell commands implementation
└── encode.go        # Encoding support (JSON, Text)
Data Flow
┌──────────────────────────────────────────┐
│              Pool                         │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  │
│  │Monitor 1│  │Monitor 2│  │Monitor N│  │
│  └────┬────┘  └────┬────┘  └────┬────┘  │
└───────┼────────────┼────────────┼────────┘
        │            │            │
        ▼            ▼            ▼
   Health Check   Health Check  Health Check
        │            │            │
        └────────────┴────────────┘
                     │
                     ▼
        ┌─────────────────────────┐
        │  Prometheus Metrics      │
        │  - Latency per monitor   │
        │  - Uptime per monitor    │
        │  - Status per monitor    │
        └─────────────────────────┘

API Reference

Pool Interface
type Pool interface {
    // Monitor Management
    MonitorAdd(mon Monitor) error
    MonitorGet(name string) Monitor
    MonitorSet(mon Monitor) error
    MonitorDel(name string)
    MonitorList() []string
    MonitorWalk(func(name string, mon Monitor) bool, validName ...string)
    
    // Lifecycle
    Start(ctx context.Context) error
    Stop(ctx context.Context) error
    Restart(ctx context.Context) error
    IsRunning() bool
    Uptime() time.Duration
    
    // Metrics
    RegisterMetrics(prom FuncGetPrometheus, log FuncLog) error
    UnregisterMetrics()
    RegisterFctProm(prom FuncGetPrometheus)
    RegisterFctLogger(log FuncLog)
    TriggerCollectMetrics(ctx context.Context, interval time.Duration)
    
    // Shell Commands
    GetShellCommand(ctx context.Context) []Command
    
    // Encoding
    MarshalText() ([]byte, error)
    MarshalJSON() ([]byte, error)
}

Usage Examples

Basic Pool Management
// Create pool
pool := pool.New(contextFunc)

// Add monitors
db := createDatabaseMonitor("postgres")
api := createAPIMonitor("user-service")
cache := createCacheMonitor("redis")

pool.MonitorAdd(db)
pool.MonitorAdd(api)
pool.MonitorAdd(cache)

// Start all
if err := pool.Start(ctx); err != nil {
    log.Fatal(err)
}
defer pool.Stop(ctx)

// Query status
for _, name := range pool.MonitorList() {
    mon := pool.MonitorGet(name)
    log.Printf("%s: %s\n", name, mon.Status())
}
Prometheus Integration
import "github.com/nabbar/golib/prometheus"

// Create Prometheus instance
prom := prometheus.New(contextFunc)

// Register metrics
err := pool.RegisterMetrics(
    func() prometheus.Prometheus { return prom },
    loggerFunc,
)
if err != nil {
    panic(err)
}

// Always cleanup
defer pool.UnregisterMetrics()

// Trigger periodic collection
go pool.TriggerCollectMetrics(ctx, 30*time.Second)

// Metrics are exported as:
// monitor_latency{monitor="postgres"}
// monitor_uptime{monitor="postgres"}
// monitor_status{monitor="postgres"}
// (and similar for each monitor)
Shell Commands
// Get shell commands
commands := pool.GetShellCommand(ctx)

// Commands available:
// 0. list    - List all monitors
// 1. info    - Show monitor information
// 2. start   - Start monitors
// 3. stop    - Stop monitors
// 4. restart - Restart monitors
// 5. status  - Show monitor status

// Execute list command
var stdout bytes.Buffer
commands[0].Run(&stdout, nil, []string{})
fmt.Println(stdout.String())

// Execute status for specific monitor
stdout.Reset()
commands[5].Run(&stdout, nil, []string{"postgres"})
fmt.Println(stdout.String())

// Execute status for all monitors
stdout.Reset()
commands[5].Run(&stdout, nil, []string{})
fmt.Println(stdout.String())
Dynamic Monitor Management
// Add monitor to running pool
if pool.IsRunning() {
    newMon := createMonitor("new-service")
    pool.MonitorAdd(newMon)  // Automatically starts if pool is running
}

// Update monitor configuration
mon := pool.MonitorGet("postgres")
mon.SetConfig(ctx, newConfig)
pool.MonitorSet(mon)

// Remove monitor
pool.MonitorDel("old-service")

// Walk through monitors
pool.MonitorWalk(func(name string, mon Monitor) bool {
    log.Printf("%s: %s (uptime: %s)\n", name, mon.Status(), mon.Uptime())
    return true  // Continue iteration
})
Batch Operations
// Start all monitors
if err := pool.Start(ctx); err != nil {
    log.Printf("Failed to start some monitors: %v\n", err)
    // Individual monitor errors are aggregated
}

// Stop all monitors
if err := pool.Stop(ctx); err != nil {
    log.Printf("Failed to stop some monitors: %v\n", err)
}

// Restart all monitors
if err := pool.Restart(ctx); err != nil {
    log.Printf("Failed to restart some monitors: %v\n", err)
}

// Check if any monitor is running
if pool.IsRunning() {
    log.Println("At least one monitor is running")
}

// Get pool uptime (longest monitor uptime)
uptime := pool.Uptime()
log.Printf("Pool uptime: %s\n", uptime)

Metrics Collection

Available Metrics

Per-monitor metrics exported to Prometheus:

Metric Type Labels Description
monitor_latency Histogram monitor Health check duration
monitor_uptime Gauge monitor Total uptime in seconds
monitor_downtime Gauge monitor Total downtime in seconds
monitor_risetime Gauge monitor Time in rising state
monitor_falltime Gauge monitor Time in falling state
monitor_status Gauge monitor Current status (0=KO, 0.5=Warn, 1=OK)
monitor_rise Gauge monitor Rising flag (0 or 1)
monitor_fall Gauge monitor Falling flag (0 or 1)
monitor_sli Gauge monitor Service Level Indicator (0-1)
Metric Names

Metrics are prefixed with a customizable name:

// Default naming
monitor_latency{monitor="postgres"}

// Custom naming (set in individual monitors)
mon.RegisterMetricsName("database_health")
// Results in: database_health_latency{monitor="postgres"}

Shell Commands

Available Commands
  1. list: List all monitors

    list
    # Output: postgres, redis, user-service
    
  2. info: Show monitor information

    info postgres
    # Output: OK: postgres (version: 14.5) | 5ms / 1h / 0s
    
    info
    # Output: (info for all monitors)
    
  3. start: Start monitors

    start postgres
    # Starts specific monitor
    
    start
    # Starts all monitors
    
  4. stop: Stop monitors

    stop postgres
    stop  # All monitors
    
  5. restart: Restart monitors

    restart postgres
    restart  # All monitors
    
  6. status: Show monitor status

    status postgres
    # Output: postgres: OK
    
    status
    # Output: (status for all monitors)
    
Integration Example
// CLI tool integration
func runCommand(pool Pool, cmdName string, args []string) {
    commands := pool.GetShellCommand(context.Background())
    
    // Find command by name
    var cmd Command
    for _, c := range commands {
        if c.Name() == cmdName {
            cmd = c
            break
        }
    }
    
    if cmd == nil {
        fmt.Printf("Unknown command: %s\n", cmdName)
        return
    }
    
    // Execute
    var stdout, stderr bytes.Buffer
    cmd.Run(&stdout, &stderr, args)
    
    fmt.Print(stdout.String())
    if stderr.Len() > 0 {
        fmt.Fprint(os.Stderr, stderr.String())
    }
}

// Usage
runCommand(pool, "list", nil)
runCommand(pool, "status", []string{"postgres"})

Performance

Benchmarks

Measured on: AMD Ryzen 9 5950X, 64GB RAM, Go 1.21

Operation Time Memory Notes
Pool Creation 2.5 µs 500 B One-time cost
MonitorAdd 1.8 µs 150 B Per monitor
MonitorGet 45 ns 0 B Lock-free read
MonitorList 2.1 µs 240 B Iteration
Start (10 monitors) 85 µs 8 KB Parallel start
Stop (10 monitors) 42 µs 2 KB Parallel stop
Metrics Collection 25 µs 1 KB All monitors
Scalability
  • Monitors per pool: Tested up to 1000 monitors
  • Concurrent operations: Lock-free reads, synchronized writes
  • Memory overhead: ~500B base + (monitors × 150B)
  • Startup time: O(n) where n = number of monitors (parallel)

Best Practices

1. Pool Lifecycle
// DO: Always cleanup
pool := pool.New(ctxFunc)
err := pool.RegisterMetrics(promFunc, logFunc)
if err != nil {
    return err
}
defer pool.UnregisterMetrics()  // Cleanup Prometheus metrics

if err := pool.Start(ctx); err != nil {
    return err
}
defer pool.Stop(ctx)  // Stop all monitors

// DON'T: Forget cleanup
pool := pool.New(ctxFunc)
pool.RegisterMetrics(promFunc, logFunc)
pool.Start(ctx)
// Missing UnregisterMetrics() and Stop()
2. Monitor Management
// DO: Check if monitor exists before operations
if mon := pool.MonitorGet("postgres"); mon != nil {
    // Use monitor
}

// DO: Handle errors from batch operations
if err := pool.Start(ctx); err != nil {
    log.Printf("Some monitors failed to start: %v\n", err)
}

// DON'T: Assume operations always succeed
pool.MonitorGet("nonexistent").Status()  // Panic!
pool.Start(ctx)  // Ignore errors
3. Metrics Registration
// DO: Register early, unregister on cleanup
pool.RegisterMetrics(promFunc, logFunc)
defer pool.UnregisterMetrics()

// DO: Use separate function for periodic collection
go pool.TriggerCollectMetrics(ctx, 30*time.Second)

// DON'T: Register multiple times without unregistering
pool.RegisterMetrics(promFunc, logFunc)
pool.RegisterMetrics(promFunc, logFunc)  // Duplicate registration error
4. Concurrent Access
// DO: Pool is thread-safe, use freely
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(idx int) {
        defer wg.Done()
        pool.MonitorGet(fmt.Sprintf("mon-%d", idx))
    }(i)
}
wg.Wait()

// DO: But be careful with monitor instances
mon := pool.MonitorGet("postgres")
// Don't share 'mon' across goroutines without synchronization
// Use pool.MonitorGet() in each goroutine instead

Testing

Test Coverage

Current coverage: 76.7%

Run tests:

# All tests
go test ./...

# With coverage
go test -coverprofile=coverage.out ./...

# With race detection
CGO_ENABLED=1 go test -race ./...

# Coverage script
./test_coverage.sh --html
Test Files
  • pool_test.go: Core functionality
  • pool_metrics_test.go: Metrics collection (extended coverage)
  • pool_shell_test.go: Shell command execution
  • pool_coverage_test.go: Edge cases and error paths
  • pool_errors_test.go: Error handling scenarios

See ../TESTING.md for detailed testing documentation.


Contributing

See ../README.md#contributing for contribution guidelines.

AI Usage Reminder: Do not use AI to generate package code. AI may assist with tests, documentation, and bug fixes only.


API Migration

Deprecated Methods

For backward compatibility, deprecated methods are still available:

// Old API (deprecated)
pool.InitMetrics(promFunc, logFunc)
defer pool.ShutDown()

// New API (recommended)
pool.RegisterMetrics(promFunc, logFunc)
defer pool.UnregisterMetrics()

The deprecated methods will be removed in a future version. Please migrate to the new API.


License

MIT License - Copyright (c) 2022 Nicolas JUHEL


Resources

Documentation

Overview

Package pool provides a thread-safe pool implementation for managing multiple health monitors.

Overview

The pool package extends the monitor functionality by providing a centralized management system for multiple monitor instances. It offers lifecycle management, metrics collection, shell command interfaces, and various encoding formats for monitor data.

Key Features

  • Thread-safe monitor management (add, get, set, delete, list, walk)
  • Lifecycle operations (start, stop, restart) for all monitors
  • Prometheus metrics integration with comprehensive health metrics
  • Shell command interface for operational management
  • JSON and text encoding support
  • Context-aware operations with proper cancellation handling

Basic Usage

import (
	"context"
	"time"

	libctx "github.com/nabbar/golib/context"
	libmon "github.com/nabbar/golib/monitor"
	moninf "github.com/nabbar/golib/monitor/info"
	monpool "github.com/nabbar/golib/monitor/pool"
	libprm "github.com/nabbar/golib/prometheus"
)

// Create context provider
ctx := context.Background()
ctxFunc := func() context.Context { return ctx }

// Create a pool
pool := monpool.New(ctxFunc)

// Create and add monitors
info, _ := moninf.New("service-1")
monitor, _ := libmon.New(ctxFunc, info)
monitor.SetHealthCheck(func(ctx context.Context) error {
	// Your health check logic
	return nil
})

pool.MonitorAdd(monitor)

// Start all monitors
pool.Start(ctx)

// Get monitor status
mon := pool.MonitorGet("service-1")
if mon != nil {
	status := mon.Status()
	fmt.Printf("Service status: %s\n", status)
}

Metrics Collection

The pool supports comprehensive Prometheus metrics collection:

// Initialize Prometheus
prom := libprm.New(ctxFunc)

// Register metrics with pool
pool.InitMetrics(func() libprm.Prometheus {
	return prom
}, loggerFunc)

// Trigger periodic metrics collection
go pool.TriggerCollectMetrics(ctx, 30*time.Second)

Available Metrics

The pool automatically collects the following metrics for all monitors:

  • monitor_latency: Health check execution time (histogram)
  • monitor_uptime: Total uptime in seconds (gauge)
  • monitor_downtime: Total downtime in seconds (gauge)
  • monitor_risetime: Time spent in rising state (gauge)
  • monitor_falltime: Time spent in falling state (gauge)
  • monitor_status: Current health status (gauge)
  • monitor_rise: Rising indicator (gauge, 0 or 1)
  • monitor_fall: Falling indicator (gauge, 0 or 1)
  • monitor_sli: Service Level Indicator with mean/min/max (gauge)

Shell Commands

The pool provides interactive shell commands for operational management:

// Get shell commands
commands := pool.GetShellCommand(ctx)

// Available commands:
// - list: Print the monitors' list
// - info: Print information about monitors
// - start: Start monitors
// - stop: Stop monitors
// - restart: Restart monitors
// - status: Print status & message for monitors

Encoding and Serialization

The pool supports multiple encoding formats:

// Marshal to JSON
jsonData, err := pool.MarshalJSON()

// Marshal to text
textData, err := pool.MarshalText()

Thread Safety

All pool operations are thread-safe and can be safely called from multiple goroutines concurrently. The pool uses internal synchronization to protect shared state.

Monitor Lifecycle

Monitors in the pool follow this lifecycle:

  1. Created and added to pool (MonitorAdd)
  2. Optionally configured with health checks and config
  3. Started individually or via pool.Start()
  4. Running: periodic health checks executed
  5. Stopped via pool.Stop() or individually
  6. Removed from pool (MonitorDel)

Context Handling

The pool respects context cancellation and timeouts:

  • All operations accept a context parameter
  • Monitor health checks use the provided context
  • Metrics collection can be cancelled via context
  • Graceful shutdown on context cancellation

Error Handling

Operations return errors when appropriate:

  • MonitorAdd: returns error if monitor has empty name or fails to start when pool is running
  • MonitorSet: returns error if monitor is nil or has empty name
  • Start/Stop/Restart: returns aggregated errors from all monitor operations
  • InitMetrics: returns error if metric registration fails

Best Practices

  • Always call Stop() to clean up resources when done
  • Use context with timeout for lifecycle operations
  • Register Prometheus and logger functions before InitMetrics
  • Handle errors from lifecycle operations
  • Use MonitorWalk for bulk operations instead of iterating manually
  • Clean up monitors (defer mon.Stop(ctx)) when adding them

Performance Considerations

  • The pool is designed for efficient concurrent access
  • Monitor operations scale linearly with the number of monitors
  • Metrics collection is optimized for large monitor counts
  • Use TriggerCollectMetrics with appropriate intervals (30s-60s recommended)

Integration with Other Packages

The pool integrates with:

  • github.com/nabbar/golib/monitor: Core monitor functionality
  • github.com/nabbar/golib/prometheus: Metrics collection
  • github.com/nabbar/golib/logger: Logging support
  • github.com/nabbar/golib/shell/command: Shell command interface
  • github.com/nabbar/golib/context: Context management

Examples

See the test files for comprehensive examples:

  • pool_test.go: Basic operations
  • pool_lifecycle_test.go: Lifecycle management
  • pool_metrics_test.go: Metrics integration
  • pool_encoding_test.go: Encoding examples
  • pool_shell_test.go: Shell command usage
  • pool_metrics_collection_test.go: Advanced metrics collection
  • pool_shell_exec_test.go: Shell command execution
  • pool_errors_test.go: Error handling patterns
  • pool_benchmark_test.go: Performance benchmarks

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ShellCommandInfo added in v1.11.3

func ShellCommandInfo() []shlcmd.CommandInfo

ShellCommandInfo returns a list of shell command descriptions available for the pool. These commands can be used to interact with the pool through a shell interface.

Types

type Pool

type Pool interface {
	montps.Pool
	libsrv.Runner

	// RegisterMetrics registers the metrics for the pool.
	// It takes a function to get the current Prometheus metrics,
	// and a function to log messages.
	// It returns an error if something went wrong during the registration.
	//
	RegisterMetrics(prm libprm.FuncGetPrometheus, log liblog.FuncLog) error

	// UnregisterMetrics removes all registered metrics from Prometheus.
	// This should be called when the pool is being shut down to clean up resources.
	UnregisterMetrics() []error

	// InitMetrics is deprecated. Use RegisterMetrics instead.
	// Deprecated: Use RegisterMetrics instead.
	InitMetrics(prm libprm.FuncGetPrometheus, log liblog.FuncLog) error

	// ShutDown is deprecated. Use UnregisterMetrics instead.
	// Deprecated: Use UnregisterMetrics instead.
	ShutDown()

	// RegisterFctProm registers a function to get the current Prometheus metrics.
	// The function should return a Prometheus object.
	//
	// This function is used to register a function to get the current Prometheus metrics.
	// The function will be used to initialize the metrics for the pool.
	//
	// It does not return an error.
	//
	// Example:
	// p.RegisterFctProm(libprm.FuncGetPrometheus(func() libprm.Prometheus {
	// 	return libprm.NewPrometheus()
	// }))
	RegisterFctProm(prm libprm.FuncGetPrometheus)
	// RegisterFctLogger registers a function to log messages.
	// The function should take a liblog.Entry and return nothing.
	//
	// This function is used to register a function to log messages.
	// The function will be used to log messages during the initialization of the metrics.
	//
	// It does not return an error.
	RegisterFctLogger(log liblog.FuncLog)
	// TriggerCollectMetrics triggers the collection of metrics for the pool.
	// It takes a context and a duration as parameters.
	// The context is used to stop the collection of metrics if the context is done.
	// The duration is used to specify the interval between two collections of metrics.
	//
	// This function is designed to be used in a goroutine.
	//
	// Example:
	// go func() {
	// 	p.TriggerCollectMetrics(context.Background(), time.Second)
	// }
	TriggerCollectMetrics(ctx context.Context, dur time.Duration)
}

func New

func New(ctx context.Context) Pool

New returns a new Pool.

The returned pool is initialized with a context provided by the ctx parameter. This context is used to initialize the config of the pool.

The returned pool is not initialized with any function to get the current Prometheus metrics. The RegisterFctProm function should be used to register such a function.

The returned pool is not initialized with any function to log messages. The RegisterFctLogger function should be used to register such a function.

Jump to

Keyboard shortcuts

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