- Introduced a new `sync` package for managing NIP-11 relay information and relay group configurations. - Implemented a cache for NIP-11 documents, allowing retrieval of relay public keys and authoritative configurations. - Enhanced the sync manager to update peer lists based on authoritative configurations from relay group events. - Updated event handling to incorporate policy checks during event imports, ensuring compliance with relay rules. - Refactored various components to utilize the new `sha256-simd` package for improved performance. - Added comprehensive tests to validate the new synchronization and group management functionalities. - Bumped version to v0.24.1 to reflect these changes.
5.6 KiB
5.6 KiB
interrupt
Graceful shutdown handling for Go applications. This package provides utilities for handling OS signals (SIGINT, SIGTERM) to enable clean shutdowns and hot reloading capabilities.
Features
- Signal Handling: Clean handling of SIGINT, SIGTERM, and SIGHUP signals
- Graceful Shutdown: Allows running goroutines to finish before exit
- Hot Reload Support: Trigger application reloads on SIGHUP
- Context Integration: Works seamlessly with Go's context package
- Custom Callbacks: Execute custom cleanup logic during shutdown
- Non-blocking: Doesn't block the main application loop
Installation
go get next.orly.dev/pkg/utils/interrupt
Usage
Basic Shutdown Handling
package main
import (
"context"
"log"
"time"
"next.orly.dev/pkg/utils/interrupt"
)
func main() {
// Create interrupt handler
handler := interrupt.New()
// Start your application
go func() {
for {
select {
case <-handler.Shutdown():
log.Println("Shutting down worker...")
return
default:
// Do work
time.Sleep(time.Second)
}
}
}()
// Wait for shutdown signal
<-handler.Done()
log.Println("Application stopped")
}
Context Integration
func worker(ctx context.Context) {
handler := interrupt.New()
// Create context that cancels on shutdown
workCtx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
<-handler.Shutdown()
cancel()
}()
// Use workCtx for all operations
for {
select {
case <-workCtx.Done():
return
default:
// Do work with context
}
}
}
Custom Shutdown Callbacks
handler := interrupt.New()
// Add cleanup callbacks
handler.OnShutdown(func() {
log.Println("Closing database connections...")
db.Close()
})
handler.OnShutdown(func() {
log.Println("Saving application state...")
saveState()
})
// Callbacks execute in reverse order when shutdown occurs
<-handler.Done()
Hot Reload Support
handler := interrupt.New()
// Handle reload signals
go func() {
for {
select {
case <-handler.Reload():
log.Println("Reloading configuration...")
reloadConfig()
case <-handler.Shutdown():
return
}
}
}()
<-handler.Done()
API Reference
Handler
The main interrupt handler type.
Methods:
New() *Handler- Create a new interrupt handlerShutdown() <-chan struct{}- Channel closed on shutdown signalsReload() <-chan struct{}- Channel closed on reload signals (SIGHUP)Done() <-chan struct{}- Channel closed when all cleanup is completeOnShutdown(func())- Add a callback to run during shutdownWait()- Block until shutdown signal receivedIsShuttingDown() bool- Check if shutdown is in progress
Signal Handling
The package handles these signals:
- SIGINT: Interrupt (Ctrl+C) - Triggers graceful shutdown
- SIGTERM: Termination - Triggers graceful shutdown
- SIGHUP: Hangup - Triggers reload (can be customized)
Shutdown Process
- Signal received (SIGINT/SIGTERM)
- Shutdown callbacks execute (in reverse order added)
- Shutdown channel closes
- Application can perform final cleanup
- Done channel closes
Testing
The interrupt package includes comprehensive tests:
Running Tests
# Run interrupt package tests
go test ./pkg/utils/interrupt
# Run with verbose output
go test -v ./pkg/utils/interrupt
# Run with race detection
go test -race ./pkg/utils/interrupt
Integration Testing
Part of the full test suite:
# Run all tests including interrupt
./scripts/test.sh
# Run specific package tests
go test ./pkg/utils/...
Test Coverage
Tests cover:
- Signal handling for all supported signals
- Callback execution order and timing
- Context cancellation
- Concurrent access patterns
- Race condition prevention
Example Test
# Test signal handling
go test -v ./pkg/utils/interrupt -run TestSignalHandling
# Test callback execution
go test -v ./pkg/utils/interrupt -run TestShutdownCallbacks
Examples
HTTP Server with Graceful Shutdown
package main
import (
"context"
"log"
"net/http"
"time"
"next.orly.dev/pkg/utils/interrupt"
)
func main() {
handler := interrupt.New()
server := &http.Server{
Addr: ":8080",
Handler: http.DefaultServeMux,
}
// Shutdown server gracefully
handler.OnShutdown(func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
server.Shutdown(ctx)
})
go server.ListenAndServe()
<-handler.Done()
log.Println("Server stopped")
}
Worker Pool with Cleanup
func main() {
handler := interrupt.New()
// Start worker pool
pool := NewWorkerPool(10)
pool.Start()
// Clean shutdown
handler.OnShutdown(func() {
log.Println("Stopping worker pool...")
pool.Stop()
})
<-handler.Done()
}
Development
Building
go build ./pkg/utils/interrupt
Code Quality
- Comprehensive test coverage
- Go best practices compliance
- Thread-safe design
- Proper signal handling
- No external dependencies
Integration
This package integrates well with:
- HTTP servers (graceful shutdown)
- Database connections (cleanup)
- Worker pools (coordination)
- Long-running services (reload capability)
License
Part of the next.orly.dev project. See main LICENSE file.