Add email background worker command

Description

This PR introduces background email processing so transactional mail is no longer sent inside the web request.

Emails are written to email_jobs, notified via Redis (cdli:queue:email), and delivered by bin/cake email_worker. When the queue is disabled, mail is still sent inline so environments without a worker keep working.

Technicalities

Queue + worker

  • New bin/cake email_worker command (--once for testing)
  • Consumes Redis with BRPOP, claims rows with FOR UPDATE
  • Falls back to DB polling when Redis is empty or a push was missed
  • Handles phpredis BRPOP timeout ([]) correctly
  • Shared EmailJobDispatcher for worker and sync fallback
  • Supported actions: welcome, resetPassword, adminNewUser, adminCrowdsourcingPrivilege, contributionDeclined
  • Transient failures retry with backoff (1 → 2 → 4 min) up to max_attempts, then failed
  • Permanent failures (UndeliverableEmailException) are marked cancelled
  • Watchdog reclaims stuck processing jobs after 10 minutes; exhausted attempts are failed instead of reclaimed forever
  • Fail-fast if email_jobs is missing (config/schema/email_jobs.sql)

Request-path safety

  • Gated by CDLI_EMAIL_QUEUE_ENABLED / EmailQueue.enabled (default: false)
  • Forgot-password uses soft success copy and revokes the reset token if enqueue fails
  • Reset URL is built in the web request and stored in the job payload

Dev infrastructure

  • app_email_worker service (dev/gea/mpi)
  • Depends on MariaDB, Redis, and Postfix

Tests

  • EmailWorkerCommandTest: claim / send / retry / fail / cancel / watchdog / priority / duplicate prevention
  • EmailQueueServiceTest: enqueue + sync fallback
  • EmailJobsTableTest: payload / allow-list validation

Fakes: FakeRedisForWorker, TimeoutFakeRedisForWorker, TestableEmailWorkerCommand, RecordingEmail

Edited by sonikagutha

Merge request reports

Loading
Loading