Summary
All three retry arms in retry.rs call metrics.record_push_retry(service_name.to_lowercase().as_str()). service_name is always a compile-time-constant &str ("APNs" or "FCM"), so to_lowercase() heap-allocates a fresh String on every retry attempt purely to lowercase a known-at-compile-time label. This runs precisely during provider 429/5xx storms and transport connect-error bursts — exactly the moments the retry path should stay allocation-light.
Location
src/push/retry.rs:207 (auth-rejection retry)
src/push/retry.rs:231 (transient 429/5xx retry)
src/push/retry.rs:409 (transport connect-retry)
Callers always pass a static string: with_retry(..., "APNs", ...) (apns.rs:387), with_retry(..., "FCM", ...) (fcm.rs:560), with_transport_retry(..., "APNs", ...) (apns.rs:600), etc.
Details
Every other push metric already routes through platform_label(platform) (dispatcher/mod.rs:72), which returns the static lowercase "apns"/"fcm" with no allocation. Only the retry path re-derives the lowercase form per attempt via to_lowercase().
Recommendation
Pass the already-lowercase static label ("apns"/"fcm") into with_retry / with_transport_retry instead of the mixed-case "APNs"/"FCM", and drop the per-attempt to_lowercase(). (The mixed-case value is still used for the human-readable warn!(service = service_name, ...) log lines, so keep that or pass both.)
Why this is not a duplicate
#316 is about the metric label semantics (transport connect-retries and provider 429/5xx retries sharing one push_retry_total label). This is strictly the per-attempt allocation, an orthogonal concern.
Impact
Minor allocation churn confined to the retry path. LOW.
Summary
All three retry arms in
retry.rscallmetrics.record_push_retry(service_name.to_lowercase().as_str()).service_nameis always a compile-time-constant&str("APNs"or"FCM"), soto_lowercase()heap-allocates a freshStringon every retry attempt purely to lowercase a known-at-compile-time label. This runs precisely during provider 429/5xx storms and transport connect-error bursts — exactly the moments the retry path should stay allocation-light.Location
src/push/retry.rs:207(auth-rejection retry)src/push/retry.rs:231(transient 429/5xx retry)src/push/retry.rs:409(transport connect-retry)Callers always pass a static string:
with_retry(..., "APNs", ...)(apns.rs:387),with_retry(..., "FCM", ...)(fcm.rs:560),with_transport_retry(..., "APNs", ...)(apns.rs:600), etc.Details
Every other push metric already routes through
platform_label(platform)(dispatcher/mod.rs:72), which returns the static lowercase"apns"/"fcm"with no allocation. Only the retry path re-derives the lowercase form per attempt viato_lowercase().Recommendation
Pass the already-lowercase static label (
"apns"/"fcm") intowith_retry/with_transport_retryinstead of the mixed-case"APNs"/"FCM", and drop the per-attemptto_lowercase(). (The mixed-case value is still used for the human-readablewarn!(service = service_name, ...)log lines, so keep that or pass both.)Why this is not a duplicate
#316 is about the metric label semantics (transport connect-retries and provider 429/5xx retries sharing one
push_retry_totallabel). This is strictly the per-attempt allocation, an orthogonal concern.Impact
Minor allocation churn confined to the retry path. LOW.