// Package notify provides a thread-safe pub/sub notification system. // // The notify package implements a lightweight, in-memory notification system // where subscribers can register to receive notifications. Each subscriber // receives notifications through a buffered channel, allowing them to process // messages at their own pace. // // # Features // // - Thread-safe concurrent operations // - Configurable notification buffer size per Notifier // - Unique subscriber IDs using cryptographic random generation // - Targeted notifications to specific subscribers // - Broadcast notifications to all subscribers // - Idempotent unsubscribe operations // - Graceful shutdown with Close() // - Zero external dependencies (uses only Go standard library) // // # Basic Usage // // Create a notifier and subscribe: // // // Create notifier with 50-notification buffer per subscriber // n := notify.NewNotifier(50) // defer n.Close() // // // Subscribe to receive notifications // sub, err := n.Subscribe() // if err != nil { // log.Fatal(err) // } // defer sub.Unsubscribe() // // // Listen for notifications // go func() { // for notification := range sub.Listen() { // fmt.Printf("Received: %s - %s\n", notification.Level, notification.Message) // } // }() // // // Send a targeted notification // n.Notify(notify.Notification{ // Target: sub.ID, // Level: notify.LevelInfo, // Message: "Hello subscriber!", // }) // // # Broadcasting // // Send notifications to all subscribers: // // // Broadcast to all subscribers // n.NotifyAll(notify.Notification{ // Level: notify.LevelSuccess, // Title: "System Update", // Message: "All systems operational", // }) // // # Notification Levels // // The package provides predefined notification levels: // // - LevelSuccess: Success messages // - LevelInfo: Informational messages // - LevelWarn: Warning messages // - LevelError: Error messages // // # Buffer Sizing // // The buffer size controls how many notifications can be queued per subscriber: // // - Small (10-25): Low latency, minimal memory, may drop messages if slow readers // - Medium (50-100): Balanced approach (recommended for most applications) // - Large (200-500): High throughput, handles bursts well // - Unbuffered (0): No queuing, sends block until received // // # Thread Safety // // All operations are thread-safe and can be called from multiple goroutines: // // - Subscribe() - Safe to call concurrently // - Unsubscribe() - Safe to call concurrently and multiple times // - Notify() - Safe to call concurrently // - NotifyAll() - Safe to call concurrently // - Close() - Safe to call concurrently and multiple times // // # Graceful Shutdown // // Close the notifier to unsubscribe all subscribers and prevent new subscriptions: // // n := notify.NewNotifier(50) // defer n.Close() // Ensures cleanup // // // Use notifier... // // On defer or explicit Close(): // // - All subscribers are removed // // - All notification channels are closed // // - Future Subscribe() calls return error // // - Notify() and NotifyAll() are no-ops // // # Notification Delivery // // Notifications are delivered using a TryLock pattern: // // - If the subscriber is available, the notification is queued // - If the subscriber is busy unsubscribing, the notification is dropped // - This prevents blocking on subscribers that are shutting down // // Buffered channels allow multiple notifications to queue, so subscribers // don't need to read immediately. Once the buffer is full, subsequent // notifications may be dropped if the subscriber is slow to read. // // # Example: Multi-Subscriber System // // func main() { // n := notify.NewNotifier(100) // defer n.Close() // // // Create multiple subscribers // for i := 0; i < 5; i++ { // sub, _ := n.Subscribe() // go func(id int, s *notify.Subscriber) { // for notif := range s.Listen() { // log.Printf("Subscriber %d: %s", id, notif.Message) // } // }(i, sub) // } // // // Broadcast to all // n.NotifyAll(notify.Notification{ // Level: notify.LevelInfo, // Message: "Server starting...", // }) // // time.Sleep(time.Second) // } // // # Error Handling // // Subscribe() returns an error in these cases: // // - Notifier is closed (error: "notifier is closed") // - Failed to generate unique ID after 10 attempts (extremely rare) // // Other operations (Notify, NotifyAll, Unsubscribe) do not return errors // and handle edge cases gracefully (e.g., notifying non-existent subscribers // is silently ignored). package notify