Skip to content

[2.x] fix: lower SMTP ping threshold to 20s to survive idle-disconnect from relays#4641

Merged
imorland merged 1 commit into2.xfrom
im/smtp-ping-threshold
May 7, 2026
Merged

[2.x] fix: lower SMTP ping threshold to 20s to survive idle-disconnect from relays#4641
imorland merged 1 commit into2.xfrom
im/smtp-ping-threshold

Conversation

@imorland
Copy link
Copy Markdown
Member

@imorland imorland commented May 7, 2026

Summary

Symfony's SmtpTransport reuses the SMTP connection across sends within a process and only NOOPs the connection when the inter-message gap exceeds pingThreshold (default 100s). AWS SES has been observed closing idle connections in the 30–90s range, so messages spaced 30–100s apart get written into a dead socket and the relay responds with:

451 4.4.2 Timeout waiting for data from client

The reporter's data (failures clustered late in the lifetime of each long-lived queue worker, never near the start) clearly fingerprints connection-age dependence.

Set pingThreshold to 20s on the Symfony transport in SmtpDriver::buildTransport() — well under any reasonable relay idle timeout, with negligible NOOP overhead.

Fixes #4614

Changes

  • framework/core/src/Mail/SmtpDriver.php — after building the transport via the factory, call $transport->setPingThreshold(20) (guarded by instanceof SmtpTransport so the cast is safe even though EsmtpTransportFactory always produces SMTP transports).

Test plan

  • Manual: configure SMTP against AWS SES; run a long-lived queue:work with a moderate trickle of email-bound notifications; confirm the 451 4.4.2 Timeout errors no longer appear in storage/logs/flarum-*.log.
  • Existing mailer integration tests pass.

No automated test for this — the bug only manifests with a real long-lived SMTP connection against a remote server with idle-disconnect behavior. Reproducing in a unit test would require simulating the transport's connection lifecycle, which is heavyweight relative to the fix.

Notes

  • 20s was chosen as a balance: well below any observed relay idle timeout (SES's lower bound is ~30s in the field), but high enough that NOOP overhead is rare. Could be made configurable via a mail_smtp_ping_threshold setting in a future PR if any operator runs into a relay needing different tuning, but the fixed default handles the common cases (SES, Mailgun, Postmark, Gmail SMTP) without ceremony.

… relays

Symfony's SmtpTransport reuses the SMTP connection across sends within
a process, NOOPing the connection only when the inter-message gap
exceeds pingThreshold (default 100s). AWS SES has been observed closing
idle connections in the 30-90s range, so messages spaced 30-100s apart
get written into a dead socket and the relay responds with
"451 4.4.2 Timeout waiting for data from client".

Set pingThreshold to 20s on the Symfony transport — well under any
reasonable relay idle timeout, NOOP overhead is negligible.

Fixes #4614
@imorland imorland requested a review from a team as a code owner May 7, 2026 01:13
@imorland imorland added this to the 2.0.0-rc.2 milestone May 7, 2026
@imorland imorland merged commit 6eb3419 into 2.x May 7, 2026
25 checks passed
@imorland imorland deleted the im/smtp-ping-threshold branch May 7, 2026 01:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SmtpDriver leaves Symfony Mailer pingThreshold at 100s default — causes intermittent 451 4.4.2 against SES

1 participant