Skip to content

danilhendrasr/rusty-mailer

Repository files navigation


Rusty Mailer

A simple but fault-tolerant and well-tested email delivery service.

Stargazers Issues MIT License

About The Project

Rusty Mailer is a practice project using Rust, it's a simple email newsletter delivery service where users can publish newsletter issues to their subscribers. It features some degree of fault-tolerance and well-tested codebase. Built using Rust and Actix Web, it was a project from the book Zero to Production in Rust.

To achieve fault-tolerance, email newsletter deliveries are done asynchronously by queueing the delivery in the database, then background workers will dequeue it periodically.

The following is the diagram:

sequenceDiagram
    participant User
    participant Rusty Mailer
    participant Database
    participant Background Worker 1
    participant Background Worker 2
    participant Postmark
    
    User->>+Rusty Mailer: Publish new newsletter issue
    Rusty Mailer-)Database: Queue the newsletter deliveries
    Rusty Mailer->>-User: "Success, newsletters will go out shortly"
    loop Email Newsletter Delivery
      par
        Background Worker 1->>Database: Dequeue
        Background Worker 1->>Postmark: Send email newsletter
      and
        Background Worker 2->>Database: Dequeue
        Background Worker 2->>Postmark: Send email newsletter
      end
    end
Loading

PostgreSQL table is utilized as the queue, background workers will pick 1 row to process everytime it does dequeue operation and then delete the row immediately after it succeeded processing. PostgreSQL's row-level locking is utilized to prevent multiple background workers from processing the same row, thus preventing duplicate email delivery.

The following diagram illustrates what would happen when there are 2 rows (row A and row B) in the table and two workers do dequeue operation concurrently:

sequenceDiagram
    participant Worker 1
    participant Worker 2
    participant Database
    
    par Worker 1 tries to process row A
      rect rgb(180, 150, 50)
        alt Row A is not locked
          Worker 1->>Database: Processes row A
        else Row A is locked
          Worker 1->>Database: Processes row B
        end
      end
    and Worker 2 tries to process row A
      rect rgb(50, 50, 50)
        alt Row A is not locked
          Worker 2->>Database: Processes row A
        else Row A is locked
          Worker 2->>Database: Processes row B
        end
      end
    end
Loading

Built With

  • Rust
  • PostgreSQL
  • Redis
  • Actix Web
  • Postmark

(back to top)

Getting Started

Prerequisites

  • Docker;
  • Rust;
  • SQLX-cli, install using cargo install sqlx-cli;
  • PostgreSQL.

Running Locally

  1. Clone the repo and cd into the directory;
  2. Run the following command:
scripts/init_db.sh && scripts/init_redis.sh
  1. Run cargo run
  2. The web can be accessed at localhost:8081;
  3. Go to localhost:8081/login to login, use "admin" for username, and "everythinghastostartsomewhere" for the password.

(back to top)

Roadmap

  • Deploy to a cloud provider
  • Create automated deployment pipeline
  • API docs (Swagger)
  • Revamp UI

See the open issues for a full list of proposed features (and known issues).

(back to top)

License

Distributed under the MIT License. See LICENSE for more information.

(back to top)

Contact

Twitter LinkedIn Email

(back to top)

Acknowledgments

Much thanks to these people/organizations/resources that made this project possible to be as it is now!

(back to top)

Releases

No releases published

Packages

No packages published