fix(notifications): SMS adapter short-circuits on first transient — kills duplicate amplification and bounds the dispatch sweep
Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
+22
-10
@@ -155,17 +155,22 @@ public sealed class SmsNotificationDeliveryAdapter : INotificationDeliveryAdapte
|
||||
httpClient, requestUri, authHeader, smsConfig, hasMessagingService,
|
||||
phone, body, timeoutSeconds, redactSecret, cancellationToken);
|
||||
|
||||
switch (attempt.Class)
|
||||
if (attempt.Class == SmsErrorClass.Transient)
|
||||
{
|
||||
case SmsErrorClass.Transient:
|
||||
firstTransientError ??= attempt.Detail;
|
||||
break;
|
||||
case SmsErrorClass.Permanent:
|
||||
permanentlyFailed.Add(phone);
|
||||
break;
|
||||
default:
|
||||
accepted.Add(phone);
|
||||
break;
|
||||
// Every recipient accepted after the first transient is a guaranteed
|
||||
// duplicate on retry (the whole notification re-sends); stopping here
|
||||
// also bounds a black-holed endpoint to one timeout per sweep.
|
||||
firstTransientError = attempt.Detail;
|
||||
break;
|
||||
}
|
||||
|
||||
if (attempt.Class == SmsErrorClass.Permanent)
|
||||
{
|
||||
permanentlyFailed.Add(phone);
|
||||
}
|
||||
else
|
||||
{
|
||||
accepted.Add(phone);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,6 +289,13 @@ public sealed class SmsNotificationDeliveryAdapter : INotificationDeliveryAdapte
|
||||
/// any transient → Transient (whole notification retries); else any
|
||||
/// accepted → Success (the permanently-failed numbers are noted, NOT parked); else
|
||||
/// (all permanent / zero accepted) → Permanent.
|
||||
/// <para>
|
||||
/// The caller's recipient loop stops at the FIRST transient rather than attempting
|
||||
/// every recipient: every recipient accepted after the first transient is a
|
||||
/// guaranteed duplicate on retry (the whole notification re-sends), so
|
||||
/// <paramref name="accepted"/> and <paramref name="permanentlyFailed"/> only ever
|
||||
/// reflect recipients attempted before that first transient.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
private DeliveryOutcome RollUp(
|
||||
string listName,
|
||||
|
||||
Reference in New Issue
Block a user