@@ -25,14 +25,16 @@ type AppleNotificationServer struct {
2525 logger * Logger
2626 ApplePushSettings ApplePushSettings
2727 sendTimeout time.Duration
28+ retryTimeout time.Duration
2829}
2930
30- func NewAppleNotificationServer (settings ApplePushSettings , logger * Logger , metrics * metrics , sendTimeoutSecs int ) * AppleNotificationServer {
31+ func NewAppleNotificationServer (settings ApplePushSettings , logger * Logger , metrics * metrics , sendTimeoutSecs int , retryTimeoutSecs int ) * AppleNotificationServer {
3132 return & AppleNotificationServer {
3233 ApplePushSettings : settings ,
3334 metrics : metrics ,
3435 logger : logger ,
3536 sendTimeout : time .Duration (sendTimeoutSecs ) * time .Second ,
37+ retryTimeout : time .Duration (retryTimeoutSecs ) * time .Second ,
3638 }
3739}
3840
@@ -227,15 +229,8 @@ func (me *AppleNotificationServer) SendNotification(msg *PushNotification) PushR
227229
228230 if me .AppleClient != nil {
229231 me .logger .Infof ("Sending apple push notification for device=%v type=%v ackId=%v" , me .ApplePushSettings .Type , msg .Type , msg .AckID )
230- start := time .Now ()
231-
232- ctx , cancel := context .WithTimeout (context .Background (), me .sendTimeout )
233- defer cancel ()
234232
235- res , err := me .AppleClient .PushWithContext (ctx , notification )
236- if me .metrics != nil {
237- me .metrics .observerNotificationResponse (PushNotifyApple , time .Since (start ).Seconds ())
238- }
233+ res , err := me .SendNotificationWithRetry (notification )
239234 if err != nil {
240235 me .logger .Errorf ("Failed to send apple push sid=%v did=%v err=%v type=%v" , msg .ServerID , msg .DeviceID , err , me .ApplePushSettings .Type )
241236 if me .metrics != nil {
@@ -269,3 +264,51 @@ func (me *AppleNotificationServer) SendNotification(msg *PushNotification) PushR
269264 }
270265 return NewOkPushResponse ()
271266}
267+
268+ func (me * AppleNotificationServer ) SendNotificationWithRetry (notification * apns.Notification ) (* apns.Response , error ) {
269+ var res * apns.Response
270+ var err error
271+ waitTime := time .Second
272+
273+ // Keep a general context to make sure the whole retry
274+ // doesn't take longer than the timeout.
275+ generalContext , cancelGeneralContext := context .WithTimeout (context .Background (), me .sendTimeout )
276+ defer cancelGeneralContext ()
277+
278+ for retries := 0 ; retries < MAX_RETRIES ; retries ++ {
279+ start := time .Now ()
280+
281+ retryContext , cancelRetryContext := context .WithTimeout (generalContext , me .retryTimeout )
282+ defer cancelRetryContext ()
283+ res , err = me .AppleClient .PushWithContext (retryContext , notification )
284+ if me .metrics != nil {
285+ me .metrics .observerNotificationResponse (PushNotifyApple , time .Since (start ).Seconds ())
286+ }
287+
288+ if err == nil {
289+ break
290+ }
291+
292+ me .logger .Errorf ("Failed to send apple push did=%v retry=%v error=%v" , notification .DeviceToken , retries , err )
293+
294+ if retries == MAX_RETRIES - 1 {
295+ me .logger .Errorf ("Max retries reached did=%v" , notification .DeviceToken )
296+ break
297+ }
298+
299+ select {
300+ case <- generalContext .Done ():
301+ case <- time .After (waitTime ):
302+ }
303+
304+ if generalContext .Err () != nil {
305+ me .logger .Infof ("Not retrying because context error did=%v retry=%v error=%v" , notification .DeviceToken , retries , generalContext .Err ())
306+ err = generalContext .Err ()
307+ break
308+ }
309+
310+ waitTime *= 2
311+ }
312+
313+ return res , err
314+ }
0 commit comments