A quiet-hours check can lose a notification in one line. The branch that skips the push writes the same sent status a real send writes, so nothing reads that row again. No retry picks it up after 07:00, and the alert history already shows it as handled.
One flag is standing in for four separate events: the app deciding to wait, a worker attempting a send, FCM accepting the request, and a device showing something. The server can observe the first three. So the skip becomes a deferral with a wake time, the attempt count stays untouched, and accepted is written only when FCM returns a message ID.
Claim each row with a short lease before sending, so a worker that dies leaves it due again rather than stuck. And compute the wake time by walking real instants minute by minute in the recipient’s IANA zone, so a clock change cannot land it on a local time that never happened.
Even then it guarantees at least one attempt, not exactly one.
https://www.duskolicanin.com/blog/firebase-push-quiet-hours-deferred-delivery