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,33 +1,174 @@
|
||||
# atomic
|
||||
|
||||
Simple wrappers for primitive types to enforce atomic access.
|
||||
Type-safe atomic operations for Go primitive types. This package provides convenient wrappers around Go's `sync/atomic` package to ensure safe concurrent access to shared variables.
|
||||
|
||||
## Features
|
||||
|
||||
- **Type Safety**: Strongly typed wrappers prevent accidental misuse
|
||||
- **Memory Safety**: Prevents race conditions in concurrent programs
|
||||
- **Performance**: Zero-overhead wrappers around standard library atomics
|
||||
- **Comprehensive Coverage**: Supports all common primitive types
|
||||
- **Thread Safe**: All operations are atomic and safe for concurrent access
|
||||
|
||||
## Installation
|
||||
|
||||
```shell
|
||||
$ go get -u github.com/mleku/nodl/pkg/atomic@latest
|
||||
```bash
|
||||
go get next.orly.dev/pkg/utils/atomic
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The standard library's `sync/atomic` is powerful, but it's easy to forget which
|
||||
variables must be accessed atomically. `github.com/mleku/nodl/pkg/atomic` preserves all the
|
||||
functionality of the standard library, but wraps the primitive types to
|
||||
provide a safer, more convenient API.
|
||||
The atomic package provides type-safe wrappers around Go's standard `sync/atomic` operations:
|
||||
|
||||
### Basic Operations
|
||||
|
||||
```go
|
||||
var atom atomic.Uint32
|
||||
atom.Store(42)
|
||||
atom.Sub(2)
|
||||
atom.CompareAndSwap(40, 11)
|
||||
import "next.orly.dev/pkg/utils/atomic"
|
||||
|
||||
// Integer types
|
||||
var counter atomic.Uint64
|
||||
counter.Store(42)
|
||||
value := counter.Load() // 42
|
||||
counter.Add(8) // 50
|
||||
|
||||
// Boolean operations
|
||||
var flag atomic.Bool
|
||||
flag.Store(true)
|
||||
if flag.Load() {
|
||||
// Handle true case
|
||||
}
|
||||
|
||||
// Pointer operations
|
||||
var ptr atomic.Pointer[string]
|
||||
ptr.Store(&someString)
|
||||
loadedPtr := ptr.Load()
|
||||
```
|
||||
|
||||
See the [documentation][doc] for a complete API specification.
|
||||
### Advanced Operations
|
||||
|
||||
## Development Status
|
||||
```go
|
||||
// Compare and swap
|
||||
var value atomic.Int64
|
||||
value.Store(100)
|
||||
swapped := value.CompareAndSwap(100, 200) // true, value is now 200
|
||||
|
||||
Stable.
|
||||
// Swap operations
|
||||
oldValue := value.Swap(300) // oldValue = 200, new value = 300
|
||||
|
||||
---
|
||||
// Atomic add/subtract
|
||||
delta := value.Add(50) // Add 50, return new value (350)
|
||||
value.Sub(25) // Subtract 25, value is now 325
|
||||
```
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
### Supported Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Bool` | Atomic boolean operations |
|
||||
| `Int32` | 32-bit signed integer |
|
||||
| `Int64` | 64-bit signed integer |
|
||||
| `Uint32` | 32-bit unsigned integer |
|
||||
| `Uint64` | 64-bit unsigned integer |
|
||||
| `Uintptr` | Pointer-sized unsigned integer |
|
||||
| `Float64` | 64-bit floating point |
|
||||
| `Pointer[T]` | Generic pointer type |
|
||||
|
||||
### Generic Pointer Example
|
||||
|
||||
```go
|
||||
// Type-safe pointer operations
|
||||
var config atomic.Pointer[Config]
|
||||
config.Store(&myConfig)
|
||||
|
||||
// Load with type safety
|
||||
currentConfig := config.Load()
|
||||
if currentConfig != nil {
|
||||
// Use config with full type safety
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
All atomic types implement a consistent interface:
|
||||
|
||||
- `Load() T` - Atomically load the current value
|
||||
- `Store(val T)` - Atomically store a new value
|
||||
- `Swap(new T) T` - Atomically swap values, return old value
|
||||
- `CompareAndSwap(old, new T) bool` - CAS operation
|
||||
|
||||
Additional methods by type:
|
||||
|
||||
**Integer Types (Int32, Int64, Uint32, Uint64):**
|
||||
- `Add(delta T) T` - Atomically add delta, return new value
|
||||
- `Sub(delta T) T` - Atomically subtract delta, return new value
|
||||
|
||||
**Float64:**
|
||||
- `Add(delta float64) float64` - Atomically add delta
|
||||
|
||||
## Testing
|
||||
|
||||
The atomic package includes comprehensive tests:
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Run atomic package tests
|
||||
go test ./pkg/utils/atomic
|
||||
|
||||
# Run with verbose output
|
||||
go test -v ./pkg/utils/atomic
|
||||
|
||||
# Run with race detection
|
||||
go test -race ./pkg/utils/atomic
|
||||
|
||||
# Run with coverage
|
||||
go test -cover ./pkg/utils/atomic
|
||||
```
|
||||
|
||||
### Integration Testing
|
||||
|
||||
Part of the full test suite:
|
||||
|
||||
```bash
|
||||
# Run all tests including atomic
|
||||
./scripts/test.sh
|
||||
|
||||
# Run specific package tests
|
||||
go test ./pkg/utils/...
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
|
||||
Tests cover:
|
||||
- All atomic operations
|
||||
- Concurrent access patterns
|
||||
- Race condition prevention
|
||||
- Type safety
|
||||
- Memory ordering guarantees
|
||||
|
||||
## Performance
|
||||
|
||||
The atomic wrappers have zero runtime overhead compared to direct `sync/atomic` calls. The Go compiler inlines all wrapper methods, resulting in identical generated code.
|
||||
|
||||
## Development
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
go build ./pkg/utils/atomic
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
|
||||
- Full test coverage with race detection
|
||||
- Go best practices compliance
|
||||
- Comprehensive documentation
|
||||
- Thread-safe by design
|
||||
|
||||
## Examples
|
||||
|
||||
See the test files for comprehensive usage examples and edge cases.
|
||||
|
||||
## License
|
||||
|
||||
Part of the next.orly.dev project. See main LICENSE file.
|
||||
|
||||
@@ -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