Email Service
The LuxMart Email Service is a standalone microservice responsible for handling all email operations with features like queuing, retries, rate limiting, and multi-language templates.
Overview
The email service is a separate Go application that processes email requests from the main API via Redis queue. It provides reliable email delivery with automatic retries, priority handling, and HTML templates.
Architecture
┌─────────────┐ ┌─────────┐ ┌───────────────┐
│ Main API │ ─────> │ Redis │ ─────> │ Email Service │
│ (Backend) │ Enqueue│ Queue │ Dequeue│ (Workers) │
└─────────────┘ └─────────┘ └───────────────┘
│
v
┌──────────────┐
│ SMTP Server │
│ (mail.ru) │
└──────────────┘
Features
1. Redis Queue System
- Priority-based email processing
- Delayed retry mechanism with exponential backoff
- Dead-letter queue for failed emails
- Queue statistics and monitoring
2. Worker Pool
- Configurable number of concurrent workers
- Parallel email processing
- Automatic template rendering
- Error handling and retry logic
3. Rate Limiting
- Token bucket algorithm
- Configurable emails per minute
- Prevents SMTP server throttling
4. Template System
- HTML email templates
- Multi-language support (en, ru, az, tr)
- Embedded template files
- Dynamic data rendering
5. Retry Mechanism
- Automatic retry for failed sends
- Exponential backoff (1s, 2s, 4s, up to 5min)
- Configurable max retry attempts
- Dead-letter queue for permanent failures
Email Types
OTP (One-Time Password)
Used for account verification, password reset, etc.
Template Files:
- otp-en.html - English
- otp-az.html - Azerbaijani
- otp-es.html - Spanish
- otp-de.html - German
Template Data:
General Email
Standard HTML or plain text emails.
Priority Levels:
- 10 (PriorityHigh) - High (processed first)
- 5 (PriorityNormal) - Normal (default)
- 0 (PriorityLow) - Low
Email Type Constants:
- email.EmailTypeOTP - One-time password emails
- email.EmailTypePasswordReset - Password reset emails
- email.EmailTypeWelcome - Welcome/onboarding emails
- email.EmailTypeOrderConfirm - Order confirmation emails
- email.EmailTypeNotification - General notification emails
- email.EmailTypeMarketing - Marketing/newsletter emails
Integration with Main API
The main API uses the email client to send emails via Redis queue:
Sending OTP Email
import "luxmart/backend/email"
// In your controller
emailClient := email.NewClient()
err := emailClient.SendOTPEmail(
"user@example.com", // to
"Verify Your Account", // subject
"123456", // otp
"Welcome! Verify your email", // message
"en", // language
)
Sending HTML Email
emailClient := email.NewClient()
err := emailClient.SendHTMLEmail(
[]string{"user@example.com"},
"Welcome to LuxMart",
"<h1>Welcome!</h1><p>Thanks for joining us.</p>",
email.EmailTypeWelcome,
email.PriorityNormal,
)
Sending Plain Text Email
emailClient := email.NewClient()
err := emailClient.SendPlainEmail(
[]string{"user@example.com"},
"Order Confirmation",
"Your order #12345 has been confirmed.",
email.EmailTypeOrderConfirm,
)
Sending Bulk Emails
recipients := []string{
"user1@example.com",
"user2@example.com",
"user3@example.com",
}
emailClient := email.NewClient()
err := emailClient.SendBulkEmail(
recipients,
"Newsletter",
"<h2>Monthly Newsletter</h2><p>Check out our latest products!</p>",
true, // isHTML
email.EmailTypeMarketing,
)
if err != nil {
log.Printf("Failed to send bulk email: %v", err)
}
Email Client API
Client Structure
Methods
SendOTPEmail
func (c *Client) SendOTPEmail(
to string,
subject string,
otp string,
message string,
language string,
) error
Sends an OTP email using the appropriate language template.
Parameters:
- to: Recipient email address
- subject: Email subject
- otp: The OTP code
- message: Additional message text
- language: Language code (en/az/es/de)
Supported Languages:
- en, english → English (default)
- az, azerbaijani → Azerbaijani
- es, spanish → Spanish
- de, german → German
SendHTMLEmail
func (c *Client) SendHTMLEmail(
to []string,
subject string,
body string,
emailType EmailType,
priority EmailPriority,
) error
Sends an HTML email.
SendPlainEmail
func (c *Client) SendPlainEmail(
to []string,
subject string,
body string,
emailType EmailType,
) error
Sends a plain text email. Priority is automatically set to PriorityNormal.
SendBulkEmail
func (c *Client) SendBulkEmail(
recipients []string,
subject string,
body string,
isHTML bool,
emailType EmailType,
) error
Sends emails to multiple recipients. Priority is automatically set to PriorityNormal. Errors for individual recipients are logged but do not stop the process.
Configuration
Environment Variables
SMTP Settings:
SMTP_HOST=smtp.mail.ru
SMTP_PORT=587
SMTPMAIL=noreply@luxmart.site
SMTPPASSWORD=your_smtp_password
SMTP_FROM=no-reply@luxmart.site
Redis Settings:
Email Service Settings:
Configuration Reference
| Variable | Default | Description |
|---|---|---|
SMTP_HOST |
smtp.mail.ru | SMTP server hostname |
SMTP_PORT |
587 | SMTP server port (use 587 for TLS) |
SMTPMAIL |
- | SMTP username/email |
SMTPPASSWORD |
- | SMTP password |
SMTP_FROM |
no-reply@luxmart.site | From email address |
REDIS_ADDR |
localhost:6379 | Redis server address |
REDIS_PASSWORD |
- | Redis password (optional) |
REDIS_DB |
0 | Redis database number |
EMAIL_MAX_RETRIES |
3 | Maximum retry attempts |
EMAIL_WORKER_COUNT |
5 | Number of worker goroutines |
EMAIL_RATE_LIMIT |
60 | Max emails per minute |
Queue Management
Queue Names
- Main Queue:
email:queue - Delayed Queue:
email:queue:delayed - Dead Letter Queue:
email:queue:dlq
Queue Operations
Enqueue Email
email := &models.Email{
ID: uuid.New().String(),
To: []string{"user@example.com"},
Subject: "Test Email",
Body: "<h1>Test</h1>",
IsHTML: true,
Priority: models.PriorityNormal,
MaxRetries: 3,
RetryCount: 0,
}
queue.Enqueue(email)
Process Delayed Emails
Automatically moves delayed emails back to main queue when ready:
Get Queue Statistics
Retry Logic
Retry Flow
- Email fails to send
RetryCountis incremented- If
RetryCount < MaxRetries: - Calculate exponential backoff delay
- Move to delayed queue
- If
RetryCount >= MaxRetries: - Move to dead-letter queue
Exponential Backoff
Dead Letter Queue (DLQ)
Permanently failed emails are moved to the DLQ for manual inspection:
Monitoring
Service Logs
The email service provides detailed logging:
[Worker 1] Processing email ID: abc-123, To: [user@example.com], Subject: Welcome
[Worker 1] Successfully sent email ID: abc-123
[Worker 2] Failed to send email ID: def-456: connection timeout
[Worker 2] Requeued email ID: def-456 (retry 1/3)
Queue Statistics
Monitor queue health via Redis:
# Check queue size
redis-cli ZCARD email:queue
# Check delayed queue
redis-cli ZCARD email:queue:delayed
# Check dead letter queue
redis-cli ZCARD email:queue:dlq
# Get stats
redis-cli GET email:stats:queued
redis-cli GET email:stats:sent
redis-cli GET email:stats:failed
Deployment
Docker Compose (Development)
email-service:
build:
context: ./services/email-service
dockerfile: Dockerfile.dev
environment:
SMTP_HOST: smtp.mail.ru
SMTP_PORT: 587
SMTPMAIL: ${SMTPMAIL}
SMTPPASSWORD: ${SMTPPASSWORD}
SMTP_FROM: no-reply@luxmart.site
REDIS_ADDR: redis:6379
EMAIL_MAX_RETRIES: 3
EMAIL_WORKER_COUNT: 5
EMAIL_RATE_LIMIT: 60
depends_on:
- redis
Docker Compose (Production)
email-service:
image: luxmart-email-service:latest
environment:
SMTP_HOST: smtp.mail.ru
SMTP_PORT: 587
SMTPMAIL: ${SMTPMAIL}
SMTPPASSWORD: ${SMTPPASSWORD}
SMTP_FROM: no-reply@luxmart.site
REDIS_ADDR: redis:6379
EMAIL_MAX_RETRIES: 3
EMAIL_WORKER_COUNT: 10
EMAIL_RATE_LIMIT: 100
restart: unless-stopped
Standalone Run
# Set environment variables
export SMTP_HOST=smtp.mail.ru
export SMTP_PORT=587
export SMTPMAIL=noreply@luxmart.site
export SMTPPASSWORD=your_password
export REDIS_ADDR=localhost:6379
# Run the service
cd services/email-service
go run main.go
Troubleshooting
Emails Not Sending
-
Check Redis connection:
-
Check email service logs:
-
Verify SMTP credentials:
-
Check queue:
High Memory Usage
- Reduce
EMAIL_WORKER_COUNT - Increase
EMAIL_RATE_LIMITdelay - Check for emails stuck in queue
Rate Limiting Issues
If you see "rate limit exceeded" errors:
- Increase
EMAIL_RATE_LIMITvalue - Or reduce
EMAIL_WORKER_COUNT - Check SMTP provider limits
Dead Letter Queue Growing
Check DLQ for common error patterns:
# Get failed emails
redis-cli ZRANGE email:queue:dlq 0 -1
# Examine one
redis-cli ZRANGE email:queue:dlq 0 0 | jq
Common causes: - Invalid email addresses - SMTP authentication failures - SMTP server rejecting emails - Network connectivity issues
Best Practices
- Use appropriate priorities:
- High: OTP, password resets
- Normal: Order confirmations, account updates
-
Low: Newsletters, promotional emails
-
Language selection:
- Always pass user's preferred language
-
Fallback to English if language not supported
-
Error handling:
- Always check for errors when enqueueing
- Log failed email attempts
-
Monitor DLQ regularly
-
Template updates:
- Test templates before deployment
- Ensure all languages are updated
-
Validate HTML rendering
-
Rate limiting:
- Respect SMTP provider limits
- Adjust rate limit based on provider
-
Monitor for throttling errors
-
Monitoring:
- Track queue sizes
- Monitor success/failure rates
- Set up alerts for DLQ growth
Support
For email service issues:
- Check logs: docker logs luxmart-email-service
- Monitor Redis: redis-cli MONITOR
- Contact: support@luxmart.site