squad

package module
v1.15.2 Latest Latest
Warning

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

Go to latest
Published: Nov 26, 2025 License: Apache-2.0, MIT Imports: 8 Imported by: 3

README ΒΆ

πŸš€ Squad

Go Reference Go Report Card

A shared shutdown primitive for graceful application lifecycle management in Go

Squad helps you orchestrate goroutines, manage graceful shutdowns, and handle application lifecycle events with minimal boilerplate.

Features β€’ Installation β€’ Quick Start β€’ Documentation β€’ Examples


πŸ“‹ Table of Contents


🎯 About

Squad is a lightweight Go package that provides a shared shutdown primitive for managing application lifecycle. It allows you to:

  • Coordinate multiple goroutines that must start and stop together
  • Handle graceful shutdowns with configurable timeouts and grace periods
  • Manage HTTP servers with proper shutdown sequences
  • Run consumer workers (e.g., message queues, event streams) with graceful stop
  • Execute bootstrap and cleanup functions for subsystems
  • Automatically handle OS signals (SIGINT, SIGTERM, SIGHUP, SIGQUIT)

Squad is particularly useful for:

  • Microservices and API servers
  • Background workers and job processors
  • Event-driven applications with multiple consumers
  • Any Go application requiring coordinated startup/shutdown

The package is designed to be Kubernetes-aware, with default grace periods aligned with Pod termination lifecycle.


✨ Features

  • ⚑ Zero dependencies - Uses only standard library and github.com/moeryomenko/synx
  • πŸ”„ Coordinated lifecycle - If one goroutine exits, others are signaled to stop
  • πŸ›‘οΈ Graceful shutdowns - Configurable grace periods and shutdown timeouts
  • 🌐 HTTP server support - Built-in wrapper for http.Server with proper shutdown
  • πŸ“¨ Consumer pattern - Graceful stop for message/event consumers without interrupting active handlers
  • 🎯 Signal handling - Automatic SIGINT/SIGTERM/SIGHUP/SIGQUIT handling
  • πŸ”Œ Bootstrap/cleanup hooks - Run initialization and cleanup functions
  • ⏱️ Kubernetes-friendly - Default 30s grace period matches k8s pod termination
  • πŸ§ͺ Well tested - Comprehensive test coverage
  • πŸ“¦ Simple API - Minimal boilerplate, intuitive usage

πŸ“¦ Installation

go get github.com/moeryomenko/squad

Requirements:

  • Go 1.25 or higher

πŸš€ Quick Start

package main

import (
    "context"
    "log"

    "github.com/moeryomenko/squad"
)

func main() {
    // Create a new squad with signal handler
    s, err := squad.New(squad.WithSignalHandler())
    if err != nil {
        log.Fatal(err)
    }

    // Run your application logic
    s.Run(func(ctx context.Context) error {
        // Your code here
        <-ctx.Done()
        return nil
    })

    // Wait for shutdown
    if err := s.Wait(); err != nil {
        log.Printf("shutdown error: %v", err)
    }
}

πŸ“– Usage

Basic Service

Create a simple service with signal handling:

s, err := squad.New(squad.WithSignalHandler())
if err != nil {
    log.Fatal(err)
}

s.Run(func(ctx context.Context) error {
    // Your background work
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-ctx.Done():
            return nil
        case <-ticker.C:
            log.Println("tick")
        }
    }
})

s.Wait()
HTTP Server

Launch an HTTP server with graceful shutdown:

s, err := squad.New(squad.WithSignalHandler())
if err != nil {
    log.Fatal(err)
}

http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
})

s.RunServer(&http.Server{Addr: ":8080"})
s.Wait()
Consumer Workers

Run consumer workers that stop gracefully without interrupting active handlers:

s, err := squad.New(squad.WithSignalHandler())
if err != nil {
    log.Fatal(err)
}

s.RunConsumer(func(consumeCtx, handleCtx context.Context) error {
    // consumeCtx: cancelled when shutdown starts
    // handleCtx: never cancelled, allows active handlers to complete

    for {
        select {
        case <-consumeCtx.Done():
            return nil
        default:
            msg := receiveMessage() // Your message source
            go processMessage(handleCtx, msg)
        }
    }
})

s.Wait()
Graceful Shutdown

Run tasks with cleanup functions:

s, err := squad.New(squad.WithSignalHandler(
    squad.WithGracefulPeriod(30 * time.Second),
    squad.WithShutdownTimeout(5 * time.Second),
))
if err != nil {
    log.Fatal(err)
}

s.RunGracefully(
    // Background function
    func(ctx context.Context) error {
        // Your work
        <-ctx.Done()
        return nil
    },
    // Cleanup function (runs on shutdown)
    func(ctx context.Context) error {
        log.Println("cleaning up...")
        return closeResources(ctx)
    },
)

s.Wait()
Bootstrap & Cleanup

Initialize and cleanup subsystems:

s, err := squad.New(
    squad.WithSignalHandler(),
    squad.WithBootstrap(
        func(ctx context.Context) error {
            return initDatabase(ctx)
        },
        func(ctx context.Context) error {
            return connectToCache(ctx)
        },
    ),
    squad.WithCloses(
        func(ctx context.Context) error {
            return closeDatabase(ctx)
        },
    ),
    squad.WithSubsystem(
        func(ctx context.Context) error { return openMessageQueue(ctx) },
        func(ctx context.Context) error { return closeMessageQueue(ctx) },
    ),
)
if err != nil {
    log.Fatal("initialization failed:", err)
}

// Your application code...

s.Wait()

πŸ” API Reference

Core Types
  • Squad - Main struct for coordinating goroutines
  • ConsumerLoop - Function signature for consumer workers
Constructor
  • New(opts ...Option) (*Squad, error) - Create a new Squad instance
Options
  • WithSignalHandler(...ShutdownOpt) - Add OS signal handling
  • WithGracefulPeriod(duration) - Set graceful shutdown period (default: 30s)
  • WithShutdownTimeout(duration) - Set shutdown timeout (default: 2s)
  • WithShutdownInGracePeriod(duration) - Set both graceful and shutdown timeout
  • WithBootstrap(...func) - Add initialization functions
  • WithCloses(...func) - Add cleanup functions
  • WithSubsystem(init, close) - Add init+cleanup pair for a subsystem
Methods
  • Run(fn) - Run a function in the squad
  • RunGracefully(backgroundFn, onDown) - Run with cleanup function
  • RunServer(*http.Server) - Run HTTP server with graceful shutdown
  • RunConsumer(ConsumerLoop) - Run consumer worker
  • Wait() error - Block until all squad members exit

πŸ’‘ Examples

See the example/ directory for complete examples:

  • simple.go - HTTP server with signal handling and graceful shutdown

Run the example:

go run example/simple.go

πŸ“ Project Structure

squad/
β”œβ”€β”€ squad.go           # Core Squad implementation
β”œβ”€β”€ consumers.go       # Consumer worker helpers
β”œβ”€β”€ options.go         # Configuration options
β”œβ”€β”€ squad_test.go      # Unit tests
β”œβ”€β”€ example/           # Example applications
β”‚   └── simple.go
β”œβ”€β”€ tools/             # Development tools
β”œβ”€β”€ go.mod             # Go module definition
β”œβ”€β”€ Makefile           # Build automation
β”œβ”€β”€ LICENSE-APACHE     # Apache 2.0 license
β”œβ”€β”€ LICENSE-MIT        # MIT license
└── README.md          # This file

πŸ›  Development

Prerequisites
  • Go 1.25+
  • golangci-lint (for linting)
  • go-mod-upgrade (for dependency management)
Commands
# Run tests
make test

# Run tests with race detector
RACE_DETECTOR=1 make test

# View coverage report
make cover

# Run linter
make lint

# Update dependencies
make mod

# View all commands
make help

πŸ§ͺ Testing

Squad includes comprehensive unit tests covering:

  • Basic lifecycle management
  • Bootstrap initialization
  • Background task failures
  • Shutdown failures and timeouts
  • Error propagation
  • Concurrent operations

Run tests:

go test ./...

# With race detector
go test -race ./...

# With coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

❓ FAQ

Q: What happens if a goroutine panics?

Squad uses synx.CtxGroup which recovers from panics and converts them to errors. The error will be returned from Wait().

Q: What's the difference between graceful period and shutdown timeout?
  • Graceful period: Total time allowed for the entire shutdown process
  • Shutdown timeout: Time reserved for executing cleanup functions

The signal handler waits gracefulPeriod - shutdownTimeout before canceling the squad context.

Q: Can I use Squad without signal handling?

Yes! Simply don't use WithSignalHandler(). You'll need to manage context cancellation yourself.

Q: How does RunConsumer differ from Run?

RunConsumer provides two contexts:

  • consumeContext: Cancelled on shutdown (stop accepting new work)
  • handleContext: Never cancelled (allow in-flight work to complete)

This enables graceful consumer shutdown without interrupting active message handlers.

Q: Is Squad safe for concurrent use?

Yes, Squad is designed to be used concurrently. Internal state is protected by mutexes.

Q: What's the default grace period?

30 seconds, matching Kubernetes pod termination grace period. This ensures compatibility with k8s deployments.


🀝 Contributing

Contributions are welcome! Please feel free to submit issues, fork the repository, and send pull requests.

Guidelines
  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for new functionality
  5. Run tests and linter (make test lint)
  6. Commit your changes
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

πŸ“„ License

This project is dual-licensed under your choice of:

You may use this project under the terms of either license.


πŸ‘€ Author

Maxim Eryomenko


If you find Squad useful, please consider giving it a ⭐️!

Made with ❀️ by Maxim Eryomenko

Documentation ΒΆ

Overview ΒΆ

helpers for run consumer workers.

Package squad contains a shared shutdown primitive.

Index ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

This section is empty.

Functions ΒΆ

This section is empty.

Types ΒΆ

type ConsumerLoop ΒΆ added in v1.11.0

type ConsumerLoop func(consumeContext, handleContext context.Context) error

ConsumerLoop is interface for run graceful consumer, which take context different context for consumer events/messages and handle them.

type Option ΒΆ added in v1.3.0

type Option func(*Squad)

Option is an option that can be applied to Squad.

func WithBootstrap ΒΆ added in v1.3.0

func WithBootstrap(fns ...func(context.Context) error) Option

WithBootstrap is a Squad option that adds bootstrap functions, which will be executed before squad started.

func WithCloses ΒΆ added in v1.3.0

func WithCloses(fns ...func(context.Context) error) Option

WithCloses is a Squad options that adds cleanup functions, which will be executed after squad stopped.

func WithSignalHandler ΒΆ

func WithSignalHandler(opts ...ShutdownOpt) Option

WithSignalHandler is a Squad option that adds signal handling goroutine to the squad. This goroutine will exit on SIGINT or SIGHUP or SIGTERM or SIGQUIT with graceful timeout and reserves time for the release of resources.

func WithSubsystem ΒΆ added in v1.9.1

func WithSubsystem(initFn, closeFn func(context.Context) error) Option

WithSubsystem is Squad option that add init and cleanup functions for given subsystem will be executed before and after squad ran.

type ShutdownOpt ΒΆ added in v1.9.0

type ShutdownOpt func(*shutdown)

ShutdownOpt is an options that can be applied to signal handler.

func WithGracefulPeriod ΒΆ added in v1.9.0

func WithGracefulPeriod(period time.Duration) ShutdownOpt

WithGracefulPeriod sets graceful shutdown period to signal handler.

func WithShutdownInGracePeriod ΒΆ added in v1.15.0

func WithShutdownInGracePeriod(timeout time.Duration) ShutdownOpt

WithShutdownInGracePeriod sets timeout for shutdown process which will be run immediately in grace period.

func WithShutdownTimeout ΒΆ added in v1.9.0

func WithShutdownTimeout(timeout time.Duration) ShutdownOpt

WithShutdownTimeout sets timeout for reserves time for the release of resource.

type Squad ΒΆ

type Squad struct {
	// contains filtered or unexported fields
}

Squad is a collection of goroutines that go up and running altogether. If one goroutine exits, other goroutines also go down.

func New ΒΆ added in v1.4.0

func New(opts ...Option) (*Squad, error)

New returns a new Squad with the context.

func (*Squad) Run ΒΆ

func (s *Squad) Run(fn func(context.Context) error)

Run runs the fn. When fn is done, it signals all the group members to stop.

func (*Squad) RunConsumer ΒΆ added in v1.11.0

func (s *Squad) RunConsumer(consumer ConsumerLoop)

RunConsumer is wrapper function for run consumer worker after receiving shutdowning signal stop context for consumer events/messages without interrupting any active handler.

func (*Squad) RunGracefully ΒΆ added in v1.2.0

func (s *Squad) RunGracefully(backgroundFn, onDown func(context.Context) error)

RunGracefully runs the backgroundFn. When fn is done, it signals all group members to stop. When stop signal has been received, squad run onDown function.

func (*Squad) RunServer ΒΆ added in v1.10.1

func (s *Squad) RunServer(srv *http.Server)

RunServer is wrapper function for launch http server.

func (*Squad) Wait ΒΆ

func (s *Squad) Wait() error

Wait blocks until all squad members exit.

Directories ΒΆ

Path Synopsis

Jump to

Keyboard shortcuts

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