Fixed deadlock where CancelByAlertIDs held nq.mu.Lock() and then called
UpdateStatus() which also tried to acquire the same lock. Now uses
direct SQL while holding the lock.
Tests added for CancelByAlertIDs:
- No matching notifications (notification stays pending)
- Matching notification cancelled
- Multiple alerts with partial match (any match cancels)
Coverage: CancelByAlertIDs 65.7% -> 81.1%
Test coverage for calculateBackoff (exponential backoff calculation),
NotificationQueueStatus constants, and QueuedNotification struct fields.
15 test cases covering backoff timing, cap behavior, and struct defaults.