Skip to content

A high-performance Go wrapper for `gopkg.in/gomail.v2` with connection pooling, persistent connections, and bulk sending capabilities.

License

Notifications You must be signed in to change notification settings

openframebox/gomail

Repository files navigation

GoMail

A high-performance Go wrapper for gopkg.in/gomail.v2 with connection pooling, persistent connections, and bulk sending capabilities.

Features

  • Persistent Connections: Reuse SMTP connections across multiple emails
  • Connection Pooling: Thread-safe connection pool for concurrent email sending
  • Bulk Sending: Send multiple emails efficiently with a single connection
  • Template Support: HTML and plain text templates with embedded FS support
  • Buffer Pooling: Optimized memory usage with sync.Pool for template rendering
  • Attachments: Support for file attachments with custom names

Installation

go get github.com/openframebox/gomail

Quick Start

Basic Usage (Single Email)

mailer, err := gomail.New(&gomail.Config{
    SMTPHost:   "localhost",
    SMTPPort:   1025,
    SMTPUser:   "user",
    SMTPPass:   "password",
    TemplateFS: templateFS, // embed.FS
})
if err != nil {
    log.Fatal(err)
}

mail := &gomail.Mail{
    Subject: "Hello",
    From:    gomail.Address{Name: "Sender", Email: "sender@example.com"},
    To:      []gomail.Address{{Name: "User", Email: "user@example.com"}},
    Template: "Welcome {{.name}}!",
    Data:     map[string]any{"name": "John"},
}

err = mailer.Send(context.Background(), mail)

Persistent Connection (Recommended for Multiple Emails)

// Open persistent connection
sender, err := mailer.Dial()
if err != nil {
    log.Fatal(err)
}
defer sender.Close()

// Send multiple emails through same connection
for i := 0; i < 100; i++ {
    mail := &gomail.Mail{
        Subject: "Email " + strconv.Itoa(i),
        From:    gomail.Address{Email: "sender@example.com"},
        To:      []gomail.Address{{Email: "user@example.com"}},
        Template: "Message {{.id}}",
        Data:     map[string]any{"id": i},
    }

    if err := sender.Send(context.Background(), mail); err != nil {
        log.Fatal(err)
    }
}

Bulk Sending (Most Efficient for Batches)

// Prepare batch
mails := make([]*gomail.Mail, 100)
for i := 0; i < 100; i++ {
    mails[i] = &gomail.Mail{
        Subject: "Bulk Email",
        From:    gomail.Address{Email: "sender@example.com"},
        To:      []gomail.Address{{Email: "user@example.com"}},
        Template: "Message {{.id}}",
        Data:     map[string]any{"id": i},
    }
}

// Send all with single connection
err = mailer.SendBulk(context.Background(), mails)

Connection Pool (Best for Concurrent Sending)

// Create pool with max 10 connections
pool := mailer.NewPool(gomail.PoolConfig{
    MaxSize: 10,
})
defer pool.Close()

// Send emails concurrently
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()

        mail := &gomail.Mail{
            Subject: "Concurrent Email",
            From:    gomail.Address{Email: "sender@example.com"},
            To:      []gomail.Address{{Email: "user@example.com"}},
            Template: "Message {{.id}}",
            Data:     map[string]any{"id": id},
        }

        if err := pool.Send(context.Background(), mail); err != nil {
            log.Printf("error: %v", err)
        }
    }(i)
}
wg.Wait()

Configuration

SMTP Config

config := &gomail.Config{
    SMTPHost:   "smtp.gmail.com",  // SMTP server host
    SMTPPort:   587,                // SMTP server port
    SMTPUser:   "user@gmail.com",   // SMTP username
    SMTPPass:   "password",         // SMTP password
    UseTLS:     true,               // Use STARTTLS
    UseSSL:     false,              // Use SSL/TLS (port 465)
    TemplateFS: templateFS,         // Template filesystem
}

Pool Config

poolConfig := gomail.PoolConfig{
    MaxSize:     10,                    // Max connections in pool
    IdleTimeout: 5 * time.Minute,       // Idle connection timeout
    MaxLifetime: 30 * time.Minute,      // Max connection lifetime
}

Templates

Templates support both HTML and plain text formats:

templates/
├── welcome.html    # HTML version
└── welcome.txt     # Plain text version
//go:embed templates/*
var templateFS embed.FS

mailer, err := gomail.New(&gomail.Config{
    // ... other config
    TemplateFS: templateFS,
})

mail := &gomail.Mail{
    Template: "welcome",  // Uses welcome.html and welcome.txt
    Data:     map[string]any{"name": "John", "code": "ABC123"},
}

Inline templates are also supported:

mail := &gomail.Mail{
    Template: "Hello {{.name}}, your code is {{.code}}",
    Data:     map[string]any{"name": "John", "code": "ABC123"},
}

Attachments

customName := "report.pdf"

mail := &gomail.Mail{
    // ... other fields
    Attachments: []gomail.Attachment{
        {Path: "/path/to/file.pdf", Name: &customName},  // Custom name
        {Path: "/path/to/data.csv"},                      // Original name
    },
}

Benchmarks

Run benchmarks with mailpit running locally:

# Start mailpit
docker run -p 1025:1025 -p 8025:8025 axllent/mailpit

# Run benchmarks
go test -bench=. -benchmem -benchtime=10s

Expected results (local mailpit):

BenchmarkSend                              100        120ms/op         # Original
BenchmarkSendWithPersistentConnection     1000         12ms/op         # 10x faster
BenchmarkSendBulk100                        50        250ms/op         # 48x faster
BenchmarkPool                              500         25ms/op         # 5x faster
BenchmarkPoolParallel                     2000         10ms/op         # 12x faster

Best Practices

1. Single Email: Use Send()

mailer.Send(ctx, mail)

2. Multiple Sequential Emails: Use persistent connection

sender, _ := mailer.Dial()
defer sender.Close()
for _, mail := range mails {
    sender.Send(ctx, mail)
}

3. Batch Sending: Use SendBulk()

mailer.SendBulk(ctx, mails)

4. Concurrent Sending: Use connection pool

pool := mailer.NewPool(gomail.PoolConfig{MaxSize: 10})
defer pool.Close()
pool.Send(ctx, mail)  // Thread-safe

License

MIT License

Contributing

Pull requests are welcome! For major changes, please open an issue first.

About

A high-performance Go wrapper for `gopkg.in/gomail.v2` with connection pooling, persistent connections, and bulk sending capabilities.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published