deliver sends a given notification message via its defined method. This method *only* returns an error when a context error occurs; any other error is interpreted as a failure to deliver the notification and as such the message will be marked as failed (to later be optionally retried).
(ctx context.Context, msg database.AcquireNotificationMessagesRow, deliver dispatch.DeliveryFunc, success, failure chan<- dispatchResult)
| 264 | // This method *only* returns an error when a context error occurs; any other error is interpreted as a failure to |
| 265 | // deliver the notification and as such the message will be marked as failed (to later be optionally retried). |
| 266 | func (n *notifier) deliver(ctx context.Context, msg database.AcquireNotificationMessagesRow, deliver dispatch.DeliveryFunc, success, failure chan<- dispatchResult) error { |
| 267 | select { |
| 268 | case <-ctx.Done(): |
| 269 | return ctx.Err() |
| 270 | default: |
| 271 | } |
| 272 | |
| 273 | ctx, cancel := context.WithTimeout(ctx, n.cfg.DispatchTimeout.Value()) |
| 274 | defer cancel() |
| 275 | logger := n.log.With(slog.F("msg_id", msg.ID), slog.F("method", msg.Method), slog.F("attempt", msg.AttemptCount+1)) |
| 276 | |
| 277 | if msg.AttemptCount > 0 { |
| 278 | n.metrics.RetryCount.WithLabelValues(string(msg.Method), msg.TemplateID.String()).Inc() |
| 279 | } |
| 280 | |
| 281 | n.metrics.InflightDispatches.WithLabelValues(string(msg.Method), msg.TemplateID.String()).Inc() |
| 282 | n.metrics.QueuedSeconds.WithLabelValues(string(msg.Method)).Observe(msg.QueuedSeconds) |
| 283 | |
| 284 | start := n.clock.Now() |
| 285 | retryable, err := deliver(ctx, msg.ID) |
| 286 | |
| 287 | n.metrics.DispatcherSendSeconds.WithLabelValues(string(msg.Method)).Observe(n.clock.Since(start).Seconds()) |
| 288 | n.metrics.InflightDispatches.WithLabelValues(string(msg.Method), msg.TemplateID.String()).Dec() |
| 289 | |
| 290 | if err != nil { |
| 291 | // Don't try to accumulate message responses if the context has been canceled. |
| 292 | // |
| 293 | // This message's lease will expire in the store and will be requeued. |
| 294 | // It's possible this will lead to a message being delivered more than once, and that is why Stop() is preferable |
| 295 | // instead of canceling the context. |
| 296 | // |
| 297 | // In the case of backpressure (i.e. the success/failure channels are full because the database is slow), |
| 298 | // we can't append any more updates to the channels otherwise this, too, will block. |
| 299 | if xerrors.Is(err, context.Canceled) { |
| 300 | return err |
| 301 | } |
| 302 | |
| 303 | select { |
| 304 | case <-ctx.Done(): |
| 305 | logger.Warn(context.Background(), "cannot record dispatch failure result", slog.Error(ctx.Err())) |
| 306 | return ctx.Err() |
| 307 | case failure <- n.newFailedDispatch(msg, err, retryable): |
| 308 | logger.Warn(ctx, "message dispatch failed", slog.Error(err)) |
| 309 | } |
| 310 | } else { |
| 311 | select { |
| 312 | case <-ctx.Done(): |
| 313 | logger.Warn(context.Background(), "cannot record dispatch success result", slog.Error(ctx.Err())) |
| 314 | return ctx.Err() |
| 315 | case success <- n.newSuccessfulDispatch(msg): |
| 316 | logger.Debug(ctx, "message dispatch succeeded") |
| 317 | } |
| 318 | } |
| 319 | n.metrics.PendingUpdates.Set(float64(len(success) + len(failure))) |
| 320 | |
| 321 | return nil |
| 322 | } |
| 323 |
no test coverage detected