Add NIP-11 relay synchronization and group management features
- 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.
This commit is contained in:
@@ -1,3 +1,291 @@
|
||||
# interrupt
|
||||
|
||||
Handle shutdowns cleanly and enable hot reload
|
||||
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
|
||||
|
||||
```bash
|
||||
go get next.orly.dev/pkg/utils/interrupt
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Shutdown Handling
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```go
|
||||
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 handler
|
||||
- `Shutdown() <-chan struct{}` - Channel closed on shutdown signals
|
||||
- `Reload() <-chan struct{}` - Channel closed on reload signals (SIGHUP)
|
||||
- `Done() <-chan struct{}` - Channel closed when all cleanup is complete
|
||||
- `OnShutdown(func())` - Add a callback to run during shutdown
|
||||
- `Wait()` - Block until shutdown signal received
|
||||
- `IsShuttingDown() 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
|
||||
|
||||
1. Signal received (SIGINT/SIGTERM)
|
||||
2. Shutdown callbacks execute (in reverse order added)
|
||||
3. Shutdown channel closes
|
||||
4. Application can perform final cleanup
|
||||
5. Done channel closes
|
||||
|
||||
## Testing
|
||||
|
||||
The interrupt package includes comprehensive tests:
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user