Case Study: Design a Distributed Email Service

“Email nhìn đơn giản — gửi thư từ A đến B. Nhưng khi 1 tỷ người gửi cùng lúc, 80% là spam, và mỗi email phải tìm được trong milliseconds — đó mới là engineering thật sự.”

Tags: system-design email case-study alex-xu-vol2 distributed-system Student: Hieu Reference: Alex Xu, System Design Interview Volume 2 — Chapter 5: Design a Distributed Email Service Prerequisite: Tuan-08-Message-Queue · Tuan-15-Data-Security-Encryption · Tuan-14-AuthN-AuthZ-Security Liên quan: Tuan-19-Design-Notification-System · Tuan-17-Design-Chat-System · Tuan-13-Monitoring-Observability


Context & Why — Tại sao Email Service quan trọng?

Analogy: Hệ thống Bưu điện Quốc gia

Hieu, hãy tưởng tượng hệ thống email như bưu điện quốc gia với quy mô toàn cầu:

Bưu điệnEmail SystemGiải thích
Người gửi thưSender (SMTP client)Soạn thư, dán tem, bỏ vào thùng thư
Bưu cục gửiOutgoing SMTP ServerNhận thư từ người gửi, kiểm tra hợp lệ, đóng dấu
Xe tải vận chuyểnSMTP relay / InternetChuyển thư qua mạng lưới giữa các bưu cục
Bưu cục nhậnIncoming SMTP ServerNhận thư đến, kiểm tra địa chỉ, phân loại
Bộ phận kiểm duyệtSpam Filter + Virus ScannerLọc thư rác, kiểm tra chất cấm (malware)
Phòng phân loạiEmail Processing PipelinePhân loại theo người nhận, folder, label
Tủ thư cá nhânMailbox StorageLưu trữ thư cho từng người, sẵn sàng để đọc
Quầy tìm thưSearch Service (Elasticsearch)Tìm kiếm thư theo nội dung, người gửi, ngày tháng
Người nhận đến lấy thưEmail Client (IMAP/POP3/API)Đọc thư, trả lời, xóa, di chuyển

Tại sao đây là bài toán khó?

Email nghe có vẻ “cũ” nhưng thực tế là một trong những distributed system phức tạp nhất:

  1. Scale khổng lồ: Gmail có 1.8 tỷ users, xử lý hàng trăm tỷ emails/ngày
  2. Spam chiếm 80%+ traffic: Phải filter trước khi đến user, nếu không mailbox sẽ ngập
  3. Search phải nhanh: User kỳ vọng tìm email trong milliseconds, dù có hàng triệu emails
  4. Reliability tuyệt đối: Mất email = mất hợp đồng, mất tiền, mất niềm tin
  5. Async by nature: Email không phải real-time — sender gửi xong không biết khi nào receiver đọc
  6. Attachment storage: File đính kèm chiếm phần lớn chi phí lưu trữ
  7. Security: Email là vector tấn công số 1 (phishing, malware, social engineering)
  8. Protocol legacy: SMTP ra đời từ 1982 (RFC 821), phải backward compatible

Hieu nhớ: Email system là write-heavy (nhận emails liên tục), read-heavy (user đọc nhiều lần), và search-heavy (tìm kiếm mọi lúc). Rất ít hệ thống phải tối ưu cả 3 workload cùng lúc.


Step 1 — Understand the Problem & Establish Design Scope

1.1 Clarifying Questions (Câu hỏi làm rõ)

Trong interview, luôn hỏi trước khi thiết kế. Dưới đây là các câu hỏi quan trọng:

Câu hỏiCâu trả lời giả địnhTại sao hỏi
Scale bao lớn?1 tỷ users (Gmail-like)Quyết định toàn bộ storage + compute strategy
Hỗ trợ gửi và nhận email?Cả hai (send + receive)Hai flow rất khác nhau
Cần email search không?Có — full-text searchSearch engine riêng (Elasticsearch)
Spam filter?Có — ML-basedSpam là 80%+ traffic, không filter = chết
Attachment support?Có — tối đa 25MB/fileBlob storage, virus scanning
Web client hay mobile?Cả hai — web + mobile + desktopAPI-first design
IMAP/POP3 support?IMAP (modern), POP3 (legacy) + REST APIMulti-protocol support
Threading/conversation?Có — Gmail-style conversation viewMessage-ID + In-Reply-To headers
Encryption?TLS in transit, encryption at restCompliance requirement
Read receipts?Optional featureTracking pixel hoặc header
Drafts & scheduled send?Storage + scheduler service

1.2 Functional Requirements (Yêu cầu chức năng)

  • FR1: Gửi email (send) — compose, add recipients (To/CC/BCC), attach files, send
  • FR2: Nhận email (receive) — accept incoming email từ bất kỳ SMTP server nào trên Internet
  • FR3: Đọc email (read) — inbox, sent, drafts, trash, custom folders/labels
  • FR4: Tìm kiếm email (search) — full-text search theo subject, body, sender, date range, attachments
  • FR5: Spam/phishing filter — tự động phân loại spam, phishing, malware
  • FR6: Attachment handling — upload, download, preview (images, PDFs)
  • FR7: Threading/conversation — group related emails thành conversation
  • FR8: Labels & folders — organize emails với labels (Gmail) hoặc folders (Outlook)
  • FR9: Sync across devices — đọc trên phone, đánh dấu read hiện trên laptop
  • FR10: Drafts & scheduled send — lưu nháp, hẹn giờ gửi
  • FR11: Auto-reply & vacation responder — trả lời tự động khi vắng mặt

1.3 Non-functional Requirements (Yêu cầu phi chức năng)

RequirementTargetGhi chú
Scalability1B users, 40B emails/day receivedBao gồm cả spam
Availability99.99% (four 9s)< 52 phút downtime/năm
Latency (send)Email gửi đi trong < 5 giâyTừ lúc user nhấn Send đến lúc vào outgoing queue
Latency (search)Search results < 500msFull-text search trên hàng triệu emails per user
Latency (read)Inbox load < 200msInbox là trang được truy cập nhiều nhất
Durability99.999999999% (eleven 9s)Không bao giờ mất email
Storage efficiencyDeduplication for attachmentsCùng 1 file gửi cho 100 người = lưu 1 lần
ComplianceCAN-SPAM, GDPR, HIPAA (healthcare)Legal requirements per region
SecurityTLS in transit, encryption at restSPF/DKIM/DMARC cho authentication

Step 2 — High-Level Design

2.1 Email Protocols Overview

Trước khi vẽ architecture, Hieu cần hiểu rõ các protocol:

SMTP — Simple Mail Transfer Protocol (Gửi email)

Thuộc tínhChi tiết
Port25 (server-to-server), 587 (client submission with STARTTLS), 465 (implicit TLS)
RFCRFC 5321 (SMTP), RFC 3207 (STARTTLS)
Vai tròChuyển email từ sender → sender’s server → receiver’s server
Đặc điểmText-based protocol, stateless per transaction, store-and-forward
Hạn chếKhông có authentication mặc định (vì ra đời 1982), push-only (không pull)

SMTP flow cơ bản:

  1. Client kết nối TCP đến SMTP server (port 587)
  2. EHLO — chào hỏi, negotiate extensions
  3. STARTTLS — upgrade lên TLS encrypted connection
  4. AUTH — authenticate user (LOGIN, PLAIN, hoặc OAuth2)
  5. MAIL FROM — khai báo sender address
  6. RCPT TO — khai báo recipient address(es)
  7. DATA — gửi nội dung email (headers + body + MIME attachments)
  8. Server trả 250 OK — email đã được accepted vào queue

IMAP — Internet Message Access Protocol (Đọc email — modern)

Thuộc tínhChi tiết
Port993 (TLS), 143 (STARTTLS)
RFCRFC 9051 (IMAP4rev2)
Vai tròClient đọc, quản lý email trên server
Đặc điểmServer-side storage — email ở trên server, client sync
Ưu điểmMulti-device sync, folder management, partial fetch
Nhược điểmPhức tạp hơn POP3, server phải lưu trữ tất cả

POP3 — Post Office Protocol v3 (Đọc email — legacy)

Thuộc tínhChi tiết
Port995 (TLS), 110 (STARTTLS)
RFCRFC 1939
Vai tròDownload email từ server về client
Đặc điểmClient-side storage — download rồi xóa trên server (mặc định)
Ưu điểmĐơn giản, ít tải server
Nhược điểmKhông sync across devices, một khi download là mất trên server

Modern API Approach (Gmail API, Microsoft Graph)

Thuộc tínhChi tiết
ProtocolHTTPS REST / GraphQL
AuthOAuth 2.0
Vai tròThay thế IMAP/POP3 cho web/mobile clients
Ưu điểmRich features (labels, threads, search), push notifications, better mobile support
Nhược điểmVendor-specific, không standard across providers

Hieu nhớ: Hệ thống email hiện đại thường dùng SMTP cho gửi (vì đó là Internet standard), nhưng REST API cho đọc (vì flexible hơn IMAP). IMAP vẫn cần hỗ trợ cho backward compatibility với các email clients cũ (Thunderbird, Outlook desktop).

So sánh IMAP vs POP3 vs API

Tiêu chíPOP3IMAPREST API
Email lưu ở đâu?ClientServerServer
Multi-device syncKhông
Offline supportTốt (đã download)Partial (cache)Cần implement
BandwidthTải hết 1 lầnTải theo nhu cầuTải theo nhu cầu
SearchClient-sideServer-sideServer-side
Push notificationKhôngIMAP IDLEWebhooks / FCM
ComplexityĐơn giảnTrung bìnhCao (nhưng flexible)

2.2 High-Level Architecture

flowchart TB
    subgraph "Email Clients"
        WEB["Web Client<br/>(React/Angular)"]
        MOB["Mobile Client<br/>(iOS/Android)"]
        DESK["Desktop Client<br/>(Thunderbird/Outlook)"]
        EXT["External SMTP<br/>Servers"]
    end

    subgraph "API & Protocol Layer"
        LB["Load Balancer<br/>(L7 — Nginx/Envoy)"]
        API["Email API Service<br/>(REST/GraphQL)"]
        SMTP_IN["SMTP Inbound Server<br/>(Receiving — Port 25)"]
        SMTP_OUT["SMTP Outbound Server<br/>(Sending — Port 587)"]
        IMAP_SRV["IMAP Server<br/>(Port 993)"]
    end

    subgraph "Processing Pipeline"
        direction TB
        SPAM["Spam Filter<br/>(ML-based)"]
        VIRUS["Virus Scanner<br/>(ClamAV)"]
        ROUTE["Email Router<br/>(Routing Rules)"]
        THREAD["Threading Service<br/>(Conversation Grouping)"]
        INDEX["Search Indexer<br/>(Async)"]
    end

    subgraph "Message Queue Layer (Kafka)"
        Q_OUT["Outgoing Queue"]
        Q_IN["Incoming Queue"]
        Q_INDEX["Index Queue"]
        Q_NOTIFY["Notification Queue"]
    end

    subgraph "Storage Layer"
        META[("Metadata DB<br/>(Cassandra/Bigtable)<br/>Subject, From, To, Date,<br/>Labels, Read/Unread")]
        BODY[("Email Body Store<br/>(Distributed Blob)<br/>HTML/Text content")]
        ATTACH[("Attachment Store<br/>(S3/GCS)<br/>Files, Images")]
        SEARCH[("Search Index<br/>(Elasticsearch/Lucene)<br/>Full-text search")]
        CACHE[("Cache Layer<br/>(Redis/Memcached)<br/>Inbox, recent emails")]
    end

    subgraph "Supporting Services"
        AUTH["Auth Service<br/>(OAuth2, SPF/DKIM)"]
        NOTIF["Push Notification<br/>(FCM/APNs)"]
        ANALYTICS["Analytics<br/>(Delivery, Bounce rates)"]
    end

    WEB & MOB --> LB
    LB --> API
    DESK --> IMAP_SRV
    DESK --> SMTP_OUT
    EXT --> SMTP_IN

    API --> Q_OUT
    SMTP_OUT --> Q_OUT
    SMTP_IN --> Q_IN

    Q_IN --> SPAM
    SPAM --> VIRUS
    VIRUS --> ROUTE
    ROUTE --> THREAD
    THREAD --> META
    THREAD --> BODY
    THREAD --> Q_INDEX
    THREAD --> Q_NOTIFY

    Q_OUT --> SMTP_OUT
    Q_INDEX --> INDEX
    INDEX --> SEARCH
    Q_NOTIFY --> NOTIF

    API --> META
    API --> BODY
    API --> ATTACH
    API --> SEARCH
    API --> CACHE
    IMAP_SRV --> META
    IMAP_SRV --> BODY

    API --> AUTH

    style SPAM fill:#f44336,stroke:#333,stroke-width:2px,color:#fff
    style SEARCH fill:#2196F3,stroke:#333,stroke-width:2px,color:#fff
    style META fill:#4CAF50,stroke:#333,stroke-width:2px,color:#fff
    style Q_IN fill:#FF9800,stroke:#333,stroke-width:2px,color:#fff
    style Q_OUT fill:#FF9800,stroke:#333,stroke-width:2px,color:#fff

2.3 Ba luồng chính (Three Main Flows)

Email system có 3 luồng riêng biệt — mỗi luồng có architecture khác nhau:

FlowProtocolHướngMô tả
Sending FlowSMTP (outbound)User → InternetUser soạn email, gửi ra ngoài
Receiving FlowSMTP (inbound)Internet → UserEmail từ bên ngoài đến mailbox
Reading FlowIMAP / REST APIServer ↔ ClientUser đọc, search, quản lý email

Step 3 — Deep Dive

3.1 Email Sending Flow (Luồng gửi email)

sequenceDiagram
    participant User as User (Client)
    participant API as Email API
    participant Auth as Auth Service
    participant Queue as Outgoing Queue<br/>(Kafka)
    participant SMTP as SMTP Outbound<br/>Server
    participant DNS as DNS Server
    participant Remote as Receiver's SMTP<br/>Server
    participant RMailbox as Receiver's<br/>Mailbox

    User->>API: POST /emails/send<br/>{to, subject, body, attachments}
    API->>Auth: Validate token + rate limit
    Auth-->>API: OK

    Note over API: Validate recipients,<br/>check attachment size,<br/>render HTML template

    API->>Queue: Enqueue outgoing email
    API-->>User: 202 Accepted<br/>(async processing)

    Queue->>SMTP: Dequeue email

    Note over SMTP: Sign email with<br/>DKIM private key

    SMTP->>DNS: MX record lookup<br/>for recipient domain
    DNS-->>SMTP: MX: mail.receiver.com<br/>Priority: 10

    SMTP->>Remote: EHLO sender.com
    Remote-->>SMTP: 250-STARTTLS

    SMTP->>Remote: STARTTLS
    Note over SMTP,Remote: TLS Handshake

    SMTP->>Remote: MAIL FROM:<user@sender.com>
    Remote-->>SMTP: 250 OK
    SMTP->>Remote: RCPT TO:<friend@receiver.com>
    Remote-->>SMTP: 250 OK
    SMTP->>Remote: DATA + email content
    Remote-->>SMTP: 250 OK — Message queued

    Note over Remote: Receiver's pipeline:<br/>SPF check → DKIM verify →<br/>Spam filter → Virus scan →<br/>Route to mailbox

    Remote->>RMailbox: Store in recipient's mailbox

Sending Flow — Chi tiết từng bước

Bước 1: User composes email

  • Client (web/mobile) gọi POST /api/v1/emails/send với payload: recipients, subject, body (HTML + plain text), attachments
  • Nếu có attachments: client upload trước lên blob storage, nhận lại attachment IDs → đính kèm vào email request

Bước 2: API Service validates

  • Authentication: verify OAuth2 token
  • Rate limiting: chống abuse (ví dụ: max 500 emails/day per user, max 100 recipients/email)
  • Validation: check email format, attachment size (max 25MB total), recipient count
  • Save to Sent folder: lưu bản copy vào sender’s mailbox metadata + body store

Bước 3: Enqueue to Outgoing Queue

  • Email được đẩy vào Kafka outgoing queue
  • Trả về 202 Accepted cho user ngay (async processing)
  • Tại sao async? Vì SMTP delivery có thể mất vài giây đến vài phút (DNS lookup, TLS handshake, remote server slow)

Bước 4: SMTP Outbound Server processes

  • Dequeue email từ Kafka
  • DKIM signing: ký email bằng private key của domain → receiver có thể verify
  • MX record lookup: query DNS để tìm receiver’s SMTP server
    • Ví dụ: gửi đến friend@gmail.com → DNS trả về alt1.gmail-smtp-in.l.google.com (priority 5)
    • Nếu primary MX server down → fallback sang server có priority cao hơn (số lớn hơn)
  • TLS negotiation: STARTTLS để encrypt connection
  • SMTP handshake: EHLO → MAIL FROM → RCPT TO → DATA

Bước 5: Error handling & retry

  • Nếu remote server trả 4xx (temporary failure): retry với exponential backoff (1min, 5min, 30min, 2h, 8h — lên đến 72 giờ)
  • Nếu remote server trả 5xx (permanent failure): generate bounce email gửi lại cho sender
  • Nếu DNS lookup fail: retry
  • Nếu TLS handshake fail: thử gửi không TLS (nếu policy cho phép) hoặc fail

Hieu nhớ: Email delivery là best-effort — không có guarantee rằng email sẽ đến. Sender chỉ biết email đã được accepted bởi receiver’s server (250 OK), không biết nó có đến inbox hay bị filter vào spam.

3.2 Email Receiving Flow (Luồng nhận email)

sequenceDiagram
    participant Remote as Sender's SMTP<br/>Server
    participant DNS as DNS Server
    participant LB as Load Balancer
    participant SMTP as SMTP Inbound<br/>Server
    participant SPF as SPF/DKIM/DMARC<br/>Validator
    participant Spam as Spam Filter<br/>(ML Model)
    participant Virus as Virus Scanner<br/>(ClamAV)
    participant Router as Email Router
    participant Thread as Threading<br/>Service
    participant Meta as Metadata DB
    participant Body as Body Store
    participant Attach as Attachment<br/>Store (S3)
    participant Index as Search Indexer
    participant Notify as Notification<br/>Service

    Remote->>DNS: MX lookup for our domain
    DNS-->>Remote: MX: mail.ourdomain.com

    Remote->>LB: TCP connection (port 25)
    LB->>SMTP: Forward to SMTP server

    SMTP-->>Remote: 220 mail.ourdomain.com ESMTP

    Remote->>SMTP: EHLO sender.com
    SMTP-->>Remote: 250-SIZE 35882577<br/>250-STARTTLS

    Remote->>SMTP: MAIL FROM:<alice@sender.com>

    Note over SMTP,SPF: Quick SPF check<br/>before accepting

    SMTP->>SPF: Check SPF record for sender.com
    SPF-->>SMTP: SPF PASS

    SMTP-->>Remote: 250 OK
    Remote->>SMTP: RCPT TO:<hieu@ourdomain.com>

    Note over SMTP: Verify recipient exists

    SMTP-->>Remote: 250 OK
    Remote->>SMTP: DATA + email content
    SMTP-->>Remote: 250 OK — Queued

    Note over SMTP: Email accepted.<br/>Now async processing begins.

    SMTP->>SPF: Full DKIM + DMARC validation
    SPF-->>SMTP: DKIM PASS, DMARC PASS

    SMTP->>Spam: Score this email
    Spam-->>SMTP: Score: 2.1 (threshold: 5.0)<br/>NOT SPAM

    SMTP->>Virus: Scan attachments
    Virus-->>SMTP: CLEAN

    SMTP->>Router: Route to mailbox
    Router->>Thread: Find/create conversation

    Note over Thread: Match Message-ID,<br/>In-Reply-To, References,<br/>Subject headers

    Thread->>Meta: Save metadata<br/>(subject, from, to, labels,<br/>conversation_id)
    Thread->>Body: Save email body<br/>(HTML + plain text)
    Thread->>Attach: Save attachments to S3
    Thread->>Index: Queue for search indexing
    Thread->>Notify: Notify user (push + badge)

Receiving Flow — Chi tiết từng bước

Bước 1: MX Record Lookup

  • Sender’s SMTP server query DNS: “Mail server nào nhận email cho ourdomain.com?”
  • DNS trả về MX records:
    • mail1.ourdomain.com — priority 10 (primary)
    • mail2.ourdomain.com — priority 20 (backup)
  • Sender kết nối đến server có priority thấp nhất (số nhỏ = ưu tiên cao)

Bước 2: SMTP Inbound accepts email

  • Load balancer (L4 — TCP level) phân phối connection đến SMTP inbound servers
  • SMTP server thực hiện initial checks trước khi accept:
    • Connection rate limiting: chống DDoS, max connections per IP
    • Reverse DNS check: sender IP có reverse DNS record không?
    • RBL check (Realtime Blackhole List): sender IP có trong blacklist không?
    • SPF quick check: sender có quyền gửi từ domain đó không?
    • Recipient validation: user hieu@ourdomain.com có tồn tại không?
  • Nếu tất cả pass → 250 OK, email vào processing queue
  • Nếu fail → 550 User not found hoặc 554 Transaction failed

Bước 3: SPF/DKIM/DMARC Validation (Chi tiết ở Section 4 — Security)

  • SPF: Kiểm tra sender’s IP có được authorize bởi domain’s DNS record không
  • DKIM: Verify chữ ký số trong email header → email không bị tamper
  • DMARC: Policy kết hợp SPF + DKIM → quyết định reject/quarantine/none

Bước 4: Spam Filter

  • Email được scoring bởi ML model (chi tiết ở Section 3.6)
  • Score thấp (< 5.0) → Inbox
  • Score trung bình (5.0 - 8.0) → Spam folder
  • Score cao (> 8.0) → Reject silently

Bước 5: Virus Scanning

  • Tất cả attachments được scan bởi antivirus engine (ClamAV hoặc tương tự)
  • Nếu phát hiện malware → quarantine email, notify admin
  • Scan cả embedded links trong HTML body → phishing detection

Bước 6: Email Routing & Storage

  • Router xác định mailbox destination dựa trên:
    • Recipient address → user’s mailbox
    • User rules (filters): “Email từ boss@company.com → label: Important”
    • Server rules: mailing list expansion, alias resolution, forwarding
  • Threading service group email vào conversation (chi tiết ở Section 3.8)
  • Lưu metadata, body, attachments vào respective stores

Bước 7: Post-processing (Async)

  • Search indexer: index email content cho full-text search
  • Notification service: push notification đến mobile, update badge count
  • Analytics: track delivery metrics

3.3 Email Reading Flow (Luồng đọc email)

Via REST API (Web/Mobile clients):

API EndpointMô tảData Source
GET /api/v1/inbox?page=1&limit=50Lấy danh sách inboxCache (Redis) → Metadata DB
GET /api/v1/emails/{id}Đọc nội dung 1 emailBody Store + Attachment Store
GET /api/v1/emails/{id}/attachments/{aid}Download attachmentAttachment Store (S3) via CDN
GET /api/v1/search?q=keywordTìm kiếm emailSearch Index (Elasticsearch)
PUT /api/v1/emails/{id}/readĐánh dấu đã đọcMetadata DB + Cache invalidation
PUT /api/v1/emails/{id}/labelsThêm/xóa labelsMetadata DB
DELETE /api/v1/emails/{id}Xóa email (move to trash)Metadata DB (soft delete)
GET /api/v1/conversations/{cid}Xem conversation threadMetadata DB (query by conversation_id)

Via IMAP (Desktop clients):

  • IMAP server đọc từ cùng Metadata DB + Body Store
  • Hỗ trợ IMAP commands: SELECT (chọn folder), FETCH (lấy email), SEARCH (tìm), STORE (flags), COPY (copy to folder)
  • IMAP IDLE: persistent connection, server push notification khi có email mới → real-time experience

Caching Strategy cho Inbox:

  • Cache what: Inbox metadata (subject, sender, date, snippet, read/unread) — không cache full body
  • Cache key: user:{user_id}:inbox:page:{n}
  • Invalidation: khi có email mới, hoặc user thay đổi label/read status
  • TTL: 5 minutes (short TTL vì inbox thay đổi thường xuyên)
  • Cache-aside pattern: check cache → miss → query DB → populate cache

3.4 Email Storage Architecture

Đây là quyết định kiến trúc quan trọng nhất cho email system. Email data có 3 loại rất khác nhau:

Traditional Storage (Legacy)

Phương phápMô tảNhược điểm
mboxTất cả email trong 1 file lớn, nối tiếp nhauFile lock contention, corruption risk, không scale
MaildirMỗi email 1 file riêng, organized trong directoriesQuá nhiều files → filesystem metadata overhead, không efficient cho search

Verdict: Cả hai đều không phù hợp cho distributed system 1B users. Cần giải pháp hiện đại.

Modern Storage Architecture

flowchart TB
    subgraph "Storage Architecture"
        direction TB

        subgraph "Metadata Store — Cassandra/Bigtable"
            M1["Row Key: user_id + email_id<br/>Columns: subject, from, to, cc, bcc,<br/>date, labels[], is_read, is_starred,<br/>conversation_id, snippet,<br/>attachment_ids[], size"]
            M2["Secondary Index:<br/>user_id + label + date (DESC)<br/>→ Inbox query nhanh"]
            M3["Secondary Index:<br/>user_id + conversation_id<br/>→ Thread grouping"]
        end

        subgraph "Email Body Store — Distributed Blob"
            B1["Key: email_id<br/>Value: HTML body + plain text<br/>Compressed with zstd"]
            B2["Average size: 5-50 KB<br/>(after compression)"]
            B3["Deduplication:<br/>Content-hash based"]
        end

        subgraph "Attachment Store — S3/GCS"
            A1["Key: content_hash<br/>Value: raw file bytes"]
            A2["Content-addressable:<br/>same file = same hash = store once"]
            A3["Virus scanned before store"]
            A4["CDN for download<br/>Pre-signed URLs (time-limited)"]
        end

        subgraph "Search Index — Elasticsearch"
            S1["Index per user shard<br/>Fields: subject, body_text,<br/>from, to, date, labels,<br/>attachment_names, has_attachment"]
        end

        subgraph "Cache — Redis Cluster"
            C1["Inbox metadata cache<br/>Recent 200 emails per user"]
            C2["Unread count per folder"]
            C3["User settings & preferences"]
        end
    end

    style M1 fill:#4CAF50,stroke:#333,color:#fff
    style B1 fill:#2196F3,stroke:#333,color:#fff
    style A1 fill:#FF9800,stroke:#333,color:#fff
    style S1 fill:#9C27B0,stroke:#333,color:#fff
    style C1 fill:#f44336,stroke:#333,color:#fff

Tại sao Cassandra/Bigtable cho Metadata?

Yêu cầuCassandra/Bigtable đáp ứng
Write-heavy (nhận email liên tục)Append-only LSM-tree → write performance tuyệt vời
Read pattern: “emails of user X, sorted by date DESC”Partition key = user_id, clustering key = date DESC → sequential read
Scale to 1B usersHorizontal scaling, consistent hashing
High availabilityMulti-datacenter replication, tunable consistency
Schema flexibilityWide-column model, dễ thêm columns (labels, flags)

Data Model (Cassandra):

ColumnTypeMô tả
user_idUUID (Partition Key)Shard by user — tất cả email của 1 user nằm cùng partition
folderTEXT (Clustering Key 1)inbox, sent, drafts, trash, spam, custom
email_dateTIMESTAMP (Clustering Key 2, DESC)Sort mới nhất trước
email_idUUID (Clustering Key 3)Unique identifier
subjectTEXTTiêu đề
from_addressTEXTNgười gửi
to_addressesLISTNgười nhận
snippetTEXT100 ký tự đầu cho preview
is_readBOOLEANĐã đọc chưa
is_starredBOOLEANĐánh dấu sao
labelsSETLabels (Important, Work, …)
conversation_idUUIDNhóm conversation
attachment_idsLISTTham chiếu đến blob store
body_refTEXTReference đến body store
size_bytesINTKích thước email (tính storage quota)

Tại sao không dùng PostgreSQL/MySQL? Relational DB đủ tốt cho < 10M users. Nhưng ở 1B users, mỗi user trung bình 10K emails → 10 nghìn tỷ rows. Sharding relational DB phức tạp hơn nhiều so với Cassandra vốn đã designed cho horizontal scaling.

Attachment Deduplication

Attachment chiếm phần lớn storage cost. Kỹ thuật deduplication rất quan trọng:

  1. Khi upload attachment → compute SHA-256 hash của file content
  2. Check hash trong metadata: đã tồn tại chưa?
    • Đã có: không upload lại, chỉ tạo reference mới → tiết kiệm storage
    • Chưa có: upload lên S3, lưu hash + S3 key
  3. Reference counting: mỗi attachment có counter “bao nhiêu emails reference nó”
  4. Khi xóa email → giảm counter. Counter = 0 → garbage collect (xóa khỏi S3)

Ví dụ thực tế: CEO gửi file báo cáo 10MB cho 500 nhân viên. Thay vì lưu 500 x 10MB = 5GB, hệ thống chỉ lưu 1 x 10MB + 500 references. Tiết kiệm 99.8% storage cho case này.

3.5 Email Search — Full-text Search at Scale

Search là tính năng quan trọng thứ hai sau read/write. User kỳ vọng tìm kiếm email nhanh như Google Search.

Search Architecture

flowchart TB
    subgraph "Search Flow"
        direction TB
        USER["User: Search 'invoice Q3'"]
        API["API Service"]
        PARSE["Query Parser<br/>→ Parse 'invoice Q3' thành<br/>structured query"]
        ES["Elasticsearch Cluster<br/>(Sharded by user_id)"]
        META["Metadata DB<br/>(Cassandra)"]
        RESULT["Return results:<br/>email_id, subject, snippet,<br/>date, highlighted matches"]
    end

    USER --> API
    API --> PARSE
    PARSE --> ES
    ES --> META
    META --> RESULT
    RESULT --> USER

    subgraph "Indexing Flow (Async)"
        direction TB
        NEW_EMAIL["New Email Arrives"]
        QUEUE["Index Queue (Kafka)"]
        INDEXER["Search Indexer<br/>(Extract text from HTML,<br/>extract attachment names,<br/>strip formatting)"]
        ES2["Elasticsearch<br/>Index Document"]
    end

    NEW_EMAIL --> QUEUE
    QUEUE --> INDEXER
    INDEXER --> ES2

    style ES fill:#9C27B0,stroke:#333,color:#fff
    style PARSE fill:#2196F3,stroke:#333,color:#fff

Indexing Strategy

Quyết địnhLựa chọnLý do
Index granularityPer-user shardingQuery luôn filter by user_id → co-locate tất cả emails của 1 user
Index fieldssubject, body_text, from, to, attachment_names, date, labelsCover mọi search use case
AnalyzerStandard analyzer + ngram cho autocompleteHỗ trợ partial match (“inv” → “invoice”)
Update strategyAsync indexing via Kafka queueKhông block email delivery, chấp nhận vài giây delay
Replication1 primary + 1 replica per shardAvailability + read throughput

Search Latency Requirements

Loại searchTarget latencyKỹ thuật
Simple keyword< 200msInverted index lookup
Full-text với operators (AND, OR, NOT)< 500msBoolean query
Date range + keyword< 500msComposite query (date filter + text match)
Attachment name search< 500msIndexed field
Autocomplete (search-as-you-type)< 100msEdge n-gram tokenizer + prefix query

Tại sao Elasticsearch/Lucene?

  1. Inverted index: Lucene’s core data structure — map từ “term” đến list of documents chứa term đó. Rất nhanh cho text search.
  2. Relevance scoring: TF-IDF / BM25 → kết quả relevant nhất hiện trước
  3. Horizontal scaling: Shard by user_id range → mỗi shard handle subset of users
  4. Near real-time: Index refresh mỗi 1 giây → email mới searchable gần ngay lập tức
  5. Rich query DSL: Boolean queries, phrase match, fuzzy match, date range, aggregations

Hieu nhớ: Search index là derived data — nó được build từ primary data (Metadata DB + Body Store). Nếu index bị corrupt → rebuild từ primary data. Không bao giờ dùng search index làm source of truth.

Search Index Size Estimation

Với 1B users, mỗi user trung bình 10K emails:

20 PB search index — đây là lý do phải shard by user_id. Mỗi Elasticsearch node handle ~1TB data hiệu quả → cần ~20,000 nodes cho search cluster (thực tế Google/Gmail dùng custom search engine, không dùng vanilla Elasticsearch).

3.6 Spam Filtering — Lọc thư rác

Spam là thách thức lớn nhất của email system. Theo thống kê, 80-85% email traffic toàn cầu là spam.

Evolution of Spam Filtering

Thế hệPhương phápƯu điểmNhược điểm
Gen 1Rule-based (keyword matching)Đơn giản, transparentDễ bypass (thay “viagra” thành “v1agra”)
Gen 2Bayesian filter (SpamAssassin)Statistical, tự họcCần training data per user, slow adapt
Gen 3ML-based (SVM, Random Forest)Accurate hơn, feature engineeringCần labeled data, model maintenance
Gen 4Deep Learning (BERT, Transformers)State-of-the-art accuracyExpensive compute, latency cao
Gen 5Ensemble + ReputationKết hợp nhiều signalsComplex pipeline

Spam Filtering Pipeline

Email đi qua nhiều layers filtering, mỗi layer bắt loại spam khác nhau:

Layer 1 — Connection-level filtering (trước khi nhận email):

  • IP reputation check: sender IP có trong blacklist (Spamhaus, SORBS) không?
  • Reverse DNS check: IP có valid PTR record không?
  • Connection rate limiting: 1 IP gửi quá nhiều connections/phút → block
  • Greylisting: lần đầu gửi → reject với “try again later”. Legit server sẽ retry, spam server thường không retry

Layer 2 — Envelope-level filtering (SMTP session):

  • SPF check: sender IP authorized cho domain?
  • Sender reputation: domain đã gửi bao nhiêu spam trước đây?
  • Recipient validation: user có tồn tại không? (chống dictionary attack)

Layer 3 — Header-level filtering (sau khi nhận DATA):

  • DKIM verification: chữ ký số hợp lệ không?
  • DMARC policy: domain owner muốn làm gì khi SPF/DKIM fail?
  • Header analysis: forged headers? Inconsistent routing?

Layer 4 — Content-level filtering (ML model):

  • Text analysis: Bayesian scoring trên subject + body text
  • URL analysis: links có dẫn đến known phishing sites không?
  • Image analysis: embedded images có chứa spam text không? (image spam)
  • Attachment analysis: file type suspicious? (.exe, .scr trong .zip?)
  • HTML analysis: hidden text? Tiny fonts? Misleading links?

Layer 5 — User-level filtering:

  • User feedback: user mark as spam → train personal model
  • Contact list: email từ known contacts → boost trust score
  • User rules: custom filters set by user

Spam Score Calculation

Trong đó:

  • = feature function thứ (SPF result, DKIM result, Bayesian score, URL score, …)
  • = weight của feature (learned từ training data)
  • Threshold mặc định: score > 5.0 → spam

SPF/DKIM/DMARC — Bộ ba authentication

ProtocolMục đíchCơ chế
SPF (Sender Policy Framework)Kiểm tra IP có quyền gửi email cho domainDomain publish DNS TXT record: “v=spf1 ip4:192.168.1.0/24 include:_spf.google.com -all”
DKIM (DomainKeys Identified Mail)Verify email không bị thay đổi trên đường truyềnSender ký email bằng private key, receiver verify bằng public key (lấy từ DNS)
DMARC (Domain-based Message Authentication)Policy: làm gì khi SPF/DKIM failDomain publish policy: “reject” (từ chối), “quarantine” (vào spam), “none” (chỉ report)

Hieu nhớ: SPF chống domain spoofing (ai đó giả làm your domain). DKIM chống email tampering (ai đó sửa nội dung). DMARC là policy layer kết hợp cả hai. Cả 3 đều dùng DNS records — zero infrastructure cost.

3.7 Attachment Handling — Xử lý file đính kèm

Upload Flow

  1. Client gửi POST /api/v1/attachments/upload (multipart/form-data)
  2. API server validate:
    • File size: max 25MB per file, max 25MB total per email
    • File type: block dangerous types (.exe, .bat, .cmd, .scr) hoặc scan kỹ hơn
  3. Virus scanning: scan file trước khi store
    • ClamAV (open-source) hoặc commercial engine
    • Nếu infected → reject upload, notify user
  4. Compute SHA-256 hash → check deduplication
  5. Upload to S3/GCS với server-side encryption (AES-256)
  6. Return attachment_id + content_hash cho client
  7. Client đính kèm attachment_id vào email khi gửi

Download Flow

  1. Client request GET /api/v1/emails/{id}/attachments/{aid}
  2. API server generate pre-signed URL (time-limited, 15 phút)
    • Pre-signed URL = URL + temporary signature → S3 cho download trực tiếp mà không qua API server
    • Giảm tải cho API server, tận dụng CDN
  3. Client download trực tiếp từ CDN/S3

Inline vs Download Attachments

LoạiKhi nàoXử lý
InlineImages trong email body (<img src="cid:...">)Embed trong HTML, load cùng email body
DownloadFiles đính kèm (PDF, DOCX, ZIP)Hiện icon + file name, user click để download
PreviewImages, PDFsGenerate thumbnail (async), hiện preview không cần download full file

Storage Tiering cho Attachments

TierStorage ClassKhi nàoChi phí
HotS3 Standard< 30 ngày tuổiCao nhất
WarmS3 Infrequent Access30-180 ngàyThấp hơn 40%
ColdS3 Glacier Instant Retrieval180 ngày - 2 nămThấp hơn 68%
ArchiveS3 Glacier Deep Archive> 2 nămThấp nhất (90%+)

Tại sao tiering quan trọng? Attachment storage chiếm 80%+ total cost của email system. User hiếm khi access attachment cũ hơn 30 ngày. Lifecycle policy tự động chuyển attachment xuống tier rẻ hơn → tiết kiệm hàng triệu USD/năm ở scale 1B users.

3.8 Threading/Conversation — Nhóm email thành cuộc hội thoại

Gmail-style conversation view là tính năng user yêu thích nhất. Thay vì hiển thị mỗi email riêng lẻ, group tất cả reply/forward thành 1 conversation.

Cơ chế: Email Headers

Mỗi email có các headers quan trọng cho threading:

HeaderMô tảVí dụ
Message-IDUnique ID cho mỗi email<abc123@mail.google.com>
In-Reply-ToMessage-ID của email đang reply<xyz789@mail.google.com>
ReferencesChuỗi Message-IDs từ đầu conversation<first@...> <second@...> <third@...>
SubjectTiêu đề (dùng làm fallback matching)Re: Project Update

Conversation Grouping Algorithm

  1. Primary matching (chính xác nhất):

    • Khi nhận email mới, check header In-Reply-ToReferences
    • Tìm email có Message-ID match → cùng conversation
  2. Fallback matching (khi headers bị thiếu/corrupt):

    • Normalize subject: bỏ Re:, Fwd:, FW:, trim whitespace
    • Match by: normalized_subject + sender/recipient overlap + time proximity (trong 30 ngày)
  3. Conversation ID assignment:

    • Email đầu tiên trong thread → tạo conversation_id mới
    • Email reply → kế thừa conversation_id từ parent email
    • Lưu conversation_id trong metadata → query tất cả emails trong conversation bằng 1 query
  4. Display ordering:

    • Trong conversation view: sort by date ASC (cũ nhất trước, mới nhất cuối)
    • Trong inbox: conversation hiển thị theo date của email mới nhất trong conversation

Edge cases phức tạp:

  • Email clients cũ không set In-Reply-To → phải fallback sang subject matching
  • Subject bị thay đổi giữa chừng (“Re: Project” → “Re: Project — URGENT”) → fuzzy matching
  • Fork conversation (reply-all rồi ai đó reply riêng) → có thể split thành sub-threads

3.9 Sync Across Devices — Đồng bộ đa thiết bị

User kỳ vọng: đọc email trên phone → laptop hiện “đã đọc” ngay lập tức.

IMAP IDLE — Server Push cho Desktop Clients

IMAP IDLE là extension cho phép server push notification khi có thay đổi:

  1. Client gửi IDLE command → connection treo, chờ
  2. Server detect email mới/flag change → gửi * EXISTS hoặc * FETCH notification
  3. Client nhận notification → fetch changes
  4. Timeout sau 29 phút → client gửi IDLE lại (RFC 2177)

Hạn chế: IDLE chỉ monitor 1 folder tại 1 thời điểm. Muốn monitor inbox + sent + drafts → cần 3 persistent connections.

Delta Sync cho Mobile/Web (API-based)

Mobile clients không dùng IMAP mà dùng delta sync qua REST API:

  1. Client giữ last_sync_timestamp (hoặc sync_token)
  2. Khi sync: GET /api/v1/sync?since={last_sync_timestamp}
  3. Server trả về list of changes kể từ timestamp đó:
    • New emails (email_id, metadata)
    • Flag changes (email_id → read/unread/starred)
    • Deleted emails (email_id → deleted)
    • Label changes (email_id → labels added/removed)
  4. Client apply changes locally → update last_sync_timestamp

Ưu điểm so với IMAP IDLE:

  • Bandwidth efficient: chỉ gửi delta (thay đổi), không gửi full state
  • Battery friendly: không cần persistent connection, sync on-demand hoặc periodic
  • Offline support: áp dụng local changes → sync khi có mạng

Offline Support

Tính năngOnlineOffline
Read cached emailsServerLocal SQLite/IndexedDB
Compose emailSend ngayQueue locally, send khi online
SearchServer-side (Elasticsearch)Local search (cached emails only)
Mark read/starredSync ngayQueue change, sync later
Download attachmentS3/CDNKhông available (trừ khi đã cache)

Conflict resolution (khi offline changes conflict với server):

  • Last-writer-wins cho simple flags (read/unread): timestamp lớn hơn thắng
  • Union merge cho labels: merge tất cả labels từ cả client và server
  • Server-wins cho destructive actions (delete): nếu server đã xóa, client phải accept

Capacity Estimation (Ước lượng dung lượng)

Assumptions (Giả thiết)

Thông sốGiá trịGiải thích
Total users1B (1 tỷ)Gmail-scale
DAU (Daily Active Users)500M50% of total users
Emails received per user per day40Bao gồm newsletters, notifications, personal
Emails sent per user per day5User gửi ít hơn nhận nhiều
Spam ratio80%80% incoming email là spam (bị filter trước khi đến mailbox)
Avg email metadata size1 KBSubject, from, to, headers, flags
Avg email body size50 KBHTML + plain text (before compression)
Avg email body compressed10 KBzstd compression ~5x ratio cho HTML
% emails có attachment20%1 in 5 emails
Avg attachment size500 KBMix of small images + large documents
Max attachment size per email25 MBStandard limit
RetentionVĩnh viễn (tối thiểu 7 năm cho compliance)Email là business record

Email Volume per Day

QPS (Queries Per Second)

Incoming SMTP QPS

Chú ý: Đây là trước spam filter. Phần lớn sẽ bị reject ở connection-level (Layer 1-2), không cần full processing.

Legitimate Email Processing QPS

Email Read QPS (API/IMAP)

Giả sử mỗi DAU check email 10 lần/ngày, mỗi lần load 20 emails:

Read QPS cao hơn write QPS 12x — cần heavy caching cho inbox metadata.

Search QPS

Giả sử 10% DAU search ít nhất 1 lần/ngày, trung bình 3 searches:

Storage Estimation

Metadata Storage (per year)

Email Body Storage (per year)

Attachment Storage (per year)

Alert: Attachment storage chiếm 90%+ total storage. Deduplication + tiering là bắt buộc.

Giả sử deduplication ratio 30% (nhiều email cùng attachment — company-wide emails, forwarded files):

Search Index Size

Accumulated over 7 years:

Total Storage Summary

ComponentPer Year7-Year Total% of Total
Metadata (Cassandra)2.92 PB20.4 PB1.3%
Email Body (Blob)29.2 PB204.4 PB13.1%
Attachments (S3)204.4 PB1,430.8 PB (~1.4 EB)79.4% (!)
Search Index (ES)5.84 PB40.9 PB2.6%
Cache (Redis)~50 TB~50 TB~0%
Total~242 PB~1,696 PB (~1.7 EB)100%

Bandwidth Estimation

Bandwidth cho attachment downloads sẽ do CDN/S3 handle trực tiếp, không qua application servers.

Tóm tắt Estimation

MetricValue
Total incoming emails/day (incl. spam)40B
Legitimate emails/day8B
Outgoing emails/day2.5B
Incoming SMTP QPS (peak)~1.39M/s
Email read QPS (peak)~3.47M/s
Search QPS (peak)~8.7K/s
Total storage/year~242 PB
Attachment storage/year~204 PB (84% of total)
Search index (7-year)~41 PB
Incoming bandwidth~45 Gbps

Security — Bảo mật Email

Email là attack vector số 1 cho cyber attacks. Theo IBM, 90%+ of cyberattacks bắt đầu bằng phishing email.

5.1 Encryption Layers

In-transit Encryption (TLS/STARTTLS)

LayerProtocolMô tả
Client → Our SMTP serverTLS 1.3 (port 465) hoặc STARTTLS (port 587)Bắt buộc cho authenticated submission
Our SMTP → Receiver’s SMTPSTARTTLS (opportunistic)Best-effort — nếu receiver không hỗ trợ TLS, có thể fallback plaintext
Client → API serverHTTPS (TLS 1.3)Bắt buộc cho web/mobile clients

Vấn đề với STARTTLS: Đây là opportunistic encryption — nếu MITM attacker strip STARTTLS capability, email gửi plaintext. Giải pháp: MTA-STS (RFC 8461) — domain publish policy yêu cầu TLS bắt buộc.

At-rest Encryption

DataEncryptionKey Management
Email bodyAES-256-GCMPer-user encryption key, managed by KMS
Attachments (S3)SSE-S3 hoặc SSE-KMSAWS KMS managed keys
Metadata (Cassandra)Transparent Data Encryption (TDE)Cluster-level key
Search IndexEncrypted at filesystem levelNode-level encryption

End-to-End Encryption (E2EE)

Cho những trường hợp yêu cầu bảo mật cao nhất (healthcare, legal, finance):

ProtocolCơ chếAdoption
PGP (Pretty Good Privacy)Public/private key pair. Sender encrypt với receiver’s public key.Low (khó dùng cho non-technical users)
S/MIMECertificate-based. Tương tự PGP nhưng dùng X.509 certificates.Medium (enterprise — Outlook supports natively)
Provider E2EE (ProtonMail)Provider manages key exchange, user giữ private keyGrowing (nhưng chỉ trong ecosystem)

Trade-off lớn nhất của E2EE: Server không thể đọc email content → không thể search server-side, không thể scan spam/virus. User phải chấp nhận search chỉ hoạt động trên client-side (đã decrypt).

5.2 Email Authentication — SPF/DKIM/DMARC (Chi tiết)

SPF (Sender Policy Framework)

Mục đích: Ngăn ai đó gửi email giả danh domain của bạn.

Cơ chế: Domain owner publish DNS TXT record liệt kê IP/server nào được phép gửi email:

ourdomain.com. IN TXT "v=spf1 ip4:192.168.1.0/24 include:_spf.google.com include:sendgrid.net -all"

Giải thích: Chỉ IP range 192.168.1.0/24, Google’s servers, và SendGrid được phép gửi email từ @ourdomain.com. -all = tất cả IP khác → FAIL.

Receiver check: Khi nhận email FROM: user@ourdomain.com → query DNS → check sender IP có trong SPF record không.

DKIM (DomainKeys Identified Mail)

Mục đích: Verify email không bị thay đổi trên đường truyền.

Cơ chế:

  1. Sender’s SMTP server tạo digital signature của email headers + body bằng private key
  2. Signature được thêm vào email header DKIM-Signature
  3. Public key published trong DNS TXT record
  4. Receiver query DNS → lấy public key → verify signature

Nếu signature invalid → email bị tamper trên đường truyền (hoặc DKIM misconfigured).

DMARC (Domain-based Message Authentication, Reporting & Conformance)

Mục đích: Cho domain owner quyết định: “Khi SPF/DKIM fail, làm gì với email đó?”

DNS record:

_dmarc.ourdomain.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@ourdomain.com; pct=100"

Policies:

  • p=none → chỉ report, không action (dùng khi mới setup, monitor)
  • p=quarantine → đưa vào spam folder
  • p=reject → từ chối hoàn toàn (strongest protection)

5.3 Anti-Phishing & Malware Defense

LayerKỹ thuậtMô tả
URL scanningReputation database + real-time checkCheck mọi URL trong email body against known phishing domains
Lookalike domain detectionPunycode analysis, visual similarityDetect goog1e.com (thay l bằng 1) hoặc Unicode homograph attacks
Attachment scanningMulti-engine AV + sandboxingScan file bằng nhiều AV engines. Suspicious files chạy trong sandbox để detect behavior
Display name spoofingHeader analysisDetect “CEO Name hacker@evil.com” — display name giả nhưng email thật khác
Business Email CompromiseML modelDetect unusual patterns: “CEO suddenly asking for wire transfer”

5.4 Data Loss Prevention (DLP)

Ngăn chặn rò rỉ dữ liệu nhạy cảm qua email:

RuleActionVí dụ
Email chứa credit card numbers (regex match)Block send + alert adminEmployee vô tình gửi file chứa PII
Email chứa SSN/CMND patternsQuarantine + require approvalHR gửi nhầm file employee data
Attachment > threshold size to external domainRequire approvalNgăn data exfiltration
Sensitive label + external recipientBlock + notifyDocument “Confidential” gửi ra ngoài company

Liên quan: Xem thêm Tuan-15-Data-Security-Encryption cho chi tiết về encryption strategies và Tuan-14-AuthN-AuthZ-Security cho authentication patterns.


DevOps — Monitoring & Observability

6.1 Key Metrics cần Monitor

Delivery Metrics

MetricMô tảTargetAlert Threshold
Delivery rate% emails delivered thành công> 99%< 98%
Bounce rate% emails bị bounce (hard + soft)< 2%> 5%
Hard bounce rate% emails bounce vì invalid address< 0.5%> 1%
Soft bounce rate% emails bounce vì temporary failure< 1.5%> 3%
Spam complaint rate% recipients mark as spam< 0.1%> 0.3%
Delivery latency (p99)Thời gian từ send đến delivered< 30s> 60s

Processing Metrics

MetricMô tảTargetAlert Threshold
Incoming SMTP QPSEmails/second nhận vàoMonitor trendSpike > 2x normal
Spam filter accuracyTrue positive rate> 99.5%< 99%
Spam false positive rateLegit email bị đánh spam< 0.01%> 0.05%
Virus detection rateInfected emails caught> 99.9%< 99.5%
Processing pipeline latency (p99)Từ SMTP accept đến mailbox< 10s> 30s
Queue depthMessages waiting in KafkaMonitor trend> 1M messages

Search Metrics

MetricMô tảTargetAlert Threshold
Search latency (p50)Median search response time< 100ms> 200ms
Search latency (p99)99th percentile< 500ms> 1000ms
Index lagTime from email received to searchable< 5s> 30s
Search availability% of time search is responsive> 99.9%< 99.5%

Infrastructure Metrics

MetricMô tảAlert Threshold
Cassandra read latency (p99)Metadata query latency> 50ms
S3 request error rateFailed attachment operations> 0.1%
Redis cache hit rateInbox cache effectiveness< 90%
Elasticsearch cluster healthGreen/Yellow/Red statusYellow > 5min
Kafka consumer lagMessages behind in processing> 100K per partition
SMTP connection pool utilizationOutbound connections in use> 80%

6.2 Alerting Strategy

SeverityResponse TimeVí dụ
P0 — Critical< 5 phútEmail delivery completely stopped, data loss detected
P1 — High< 15 phútDelivery rate drop > 10%, spam filter down
P2 — Medium< 1 giờSearch latency degraded, queue depth growing
P3 — LowNext business dayCache hit rate drop, storage approaching threshold

6.3 Dashboards cần có

  1. Email Flow Dashboard: real-time incoming/outgoing QPS, delivery success rate, bounce rate
  2. Spam Dashboard: spam volume, filter accuracy, false positive count, top spam sources
  3. Search Dashboard: search QPS, latency percentiles, index lag, cluster health
  4. Storage Dashboard: total storage by type, growth trend, storage tiering distribution
  5. Security Dashboard: SPF/DKIM/DMARC pass rates, phishing attempts blocked, malware detected
  6. User Experience Dashboard: inbox load time, email open latency, sync latency

Liên quan: Xem thêm Tuan-13-Monitoring-Observability cho observability patterns (logs, metrics, traces).


Mermaid Diagrams — Tổng hợp Architecture

Email Sending Flow (Simplified)

flowchart LR
    A["Sender<br/>(compose email)"] --> B["API Server<br/>(validate, rate limit)"]
    B --> C["Outgoing Queue<br/>(Kafka)"]
    C --> D["SMTP Outbound<br/>(DKIM sign)"]
    D --> E["DNS Lookup<br/>(MX record)"]
    E --> F["Receiver's SMTP<br/>Server"]
    F --> G["Receiver's<br/>Processing Pipeline"]
    G --> H["Receiver's<br/>Mailbox"]

    D -->|"Bounce"| I["Bounce Handler<br/>(notify sender)"]
    C -->|"Retry"| D

    style C fill:#FF9800,stroke:#333,color:#fff
    style D fill:#4CAF50,stroke:#333,color:#fff
    style F fill:#2196F3,stroke:#333,color:#fff

Email Receiving Pipeline (Detailed)

flowchart TB
    A["External SMTP Server"] --> B["Load Balancer<br/>(L4 TCP)"]
    B --> C["SMTP Inbound Server"]

    C --> D{"Connection-level<br/>Check"}
    D -->|"IP blacklisted"| REJECT1["550 Blocked"]
    D -->|"Pass"| E{"SPF Check"}
    E -->|"Fail + DMARC reject"| REJECT2["550 SPF Failed"]
    E -->|"Pass/Softfail"| F["Accept DATA"]

    F --> G["Incoming Queue<br/>(Kafka)"]

    G --> H["DKIM Verify"]
    H --> I["DMARC Evaluate"]
    I --> J["Spam Filter<br/>(ML Scoring)"]

    J -->|"Score > 8.0"| SPAM_REJECT["Silently Discard"]
    J -->|"Score 5.0 - 8.0"| SPAM_FOLDER["→ Spam Folder"]
    J -->|"Score < 5.0"| K["Virus Scanner"]

    K -->|"Infected"| QUARANTINE["Quarantine<br/>(alert admin)"]
    K -->|"Clean"| L["Email Router"]

    L --> M["Apply User Rules<br/>(filters, labels)"]
    M --> N["Threading Service<br/>(conversation grouping)"]

    N --> O["Save Metadata<br/>(Cassandra)"]
    N --> P["Save Body<br/>(Blob Store)"]
    N --> Q["Save Attachments<br/>(S3)"]
    N --> R["Queue for Search<br/>Indexing"]
    N --> S["Push Notification<br/>(FCM/APNs)"]

    R --> T["Elasticsearch<br/>Indexer"]

    style G fill:#FF9800,stroke:#333,color:#fff
    style J fill:#f44336,stroke:#333,color:#fff
    style T fill:#9C27B0,stroke:#333,color:#fff
    style O fill:#4CAF50,stroke:#333,color:#fff

Storage Architecture Overview

flowchart TB
    subgraph "Write Path"
        W1["Email arrives"] --> W2["Processing Pipeline"]
        W2 --> W3["Metadata Writer"]
        W2 --> W4["Body Writer"]
        W2 --> W5["Attachment Writer"]
        W2 --> W6["Index Writer"]
    end

    subgraph "Storage Engines"
        W3 --> META[("Cassandra Cluster<br/>────────────<br/>Partition: user_id<br/>Sort: date DESC<br/>────────────<br/>3 replicas<br/>Consistency: LOCAL_QUORUM")]

        W4 --> BODY[("Distributed Blob Store<br/>────────────<br/>Key: email_id<br/>Compressed: zstd<br/>────────────<br/>3 replicas<br/>Encrypted: AES-256")]

        W5 --> S3[("S3 / GCS<br/>────────────<br/>Key: content_hash<br/>Dedup: SHA-256<br/>────────────<br/>11 nines durability<br/>Lifecycle tiering")]

        W6 --> ES[("Elasticsearch<br/>────────────<br/>Shard by: user_id range<br/>Fields: subject, body,<br/>from, to, labels<br/>────────────<br/>1 primary + 1 replica")]
    end

    subgraph "Read Path"
        R1["User reads inbox"] --> R2["Cache Check<br/>(Redis)"]
        R2 -->|"Hit"| R3["Return cached<br/>metadata"]
        R2 -->|"Miss"| META
        META --> R3

        R4["User reads email"] --> BODY
        R5["User downloads file"] --> CDN["CDN<br/>(CloudFront)"]
        CDN --> S3

        R6["User searches"] --> ES
    end

    style META fill:#4CAF50,stroke:#333,color:#fff
    style BODY fill:#2196F3,stroke:#333,color:#fff
    style S3 fill:#FF9800,stroke:#333,color:#fff
    style ES fill:#9C27B0,stroke:#333,color:#fff
    style R2 fill:#f44336,stroke:#333,color:#fff

Search Architecture (Detailed)

flowchart TB
    subgraph "Indexing Pipeline (Write)"
        direction TB
        NE["New Email"] --> KQ["Kafka Index Queue"]
        KQ --> IDX["Indexer Workers<br/>(Stateless, auto-scaled)"]
        IDX --> EXTRACT["Text Extraction:<br/>1. Strip HTML tags<br/>2. Decode MIME<br/>3. Extract attachment names<br/>4. Normalize Unicode"]
        EXTRACT --> ANALYZE["Analysis Pipeline:<br/>1. Tokenize<br/>2. Lowercase<br/>3. Remove stop words<br/>4. Stem (porter)"]
        ANALYZE --> ES_WRITE["Elasticsearch<br/>Index Write"]
    end

    subgraph "Query Pipeline (Read)"
        direction TB
        USER["User: search 'invoice 2025'"] --> API["API Server"]
        API --> QP["Query Parser:<br/>1. Extract operators<br/>  (from:, to:, has:attachment)<br/>2. Date range extraction<br/>3. Build Elasticsearch DSL"]
        QP --> ES_READ["Elasticsearch<br/>Query Execution"]
        ES_READ --> RANK["Ranking:<br/>1. BM25 text relevance<br/>2. Recency boost<br/>3. User interaction signals"]
        RANK --> ENRICH["Enrich Results:<br/>Fetch metadata from<br/>Cassandra for display"]
        ENRICH --> RESULT["Return:<br/>email_id, subject, snippet,<br/>date, highlighted matches"]
    end

    ES_WRITE --> ES_CLUSTER[("Elasticsearch Cluster<br/>────────────<br/>20,000+ nodes<br/>Sharded by user_id range<br/>1 primary + 1 replica")]
    ES_READ --> ES_CLUSTER

    style KQ fill:#FF9800,stroke:#333,color:#fff
    style ES_CLUSTER fill:#9C27B0,stroke:#333,color:#fff
    style QP fill:#2196F3,stroke:#333,color:#fff

Aha Moments & Pitfalls — Bài học quan trọng

Aha Moments (Khoảnh khắc “à ha!“)

1. Email là async by nature — Queue-based architecture là tự nhiên

Aha: Khác với chat (cần real-time), email vốn dĩ là async. Sender gửi xong không kỳ vọng receiver đọc ngay. Điều này có nghĩa:

  • Message queue (Kafka) là backbone tự nhiên — không phải workaround
  • “Email gửi rồi nhưng chưa đến” là expected behavior (RFC cho phép retry đến 72 giờ)
  • Retry strategy cực kỳ quan trọng — exponential backoff, bounce handling

2. Spam chiếm 80%+ traffic — Filter trước, process sau

Aha: Nếu hệ thống process tất cả incoming email trước khi filter spam, sẽ cần 5x resources. Architecture phải filter spam ở tầng sớm nhất (connection-level, trước cả khi accept DATA). Đây là lý do có multi-layer filtering: mỗi layer rẻ hơn (CPU cost) sẽ filter trước, layer đắt (ML model) filter sau.

3. Search at scale là bài toán riêng — Không thể dùng database query

Aha: SQL LIKE '%keyword%' trên 10 trillion emails sẽ mất hàng giờ. Search engine (Elasticsearch/Lucene) với inverted index là hoàn toàn khác biệt:

  • Build index lúc write → query nhanh lúc read
  • Index là derived data — rebuild được từ primary store
  • Shard by user_id → mỗi search chỉ hit 1-2 shards

4. Attachment storage dominates cost — 90% chi phí là file đính kèm

Aha: Metadata + email body chỉ chiếm ~15% storage. Attachments chiếm ~85%. Ba kỹ thuật tiết kiệm nhất:

  1. Deduplication: content-hash → lưu 1 lần dù gửi cho 1000 người
  2. Storage tiering: hot → warm → cold → archive (lifecycle policy)
  3. Compression: không compress file đã compressed (images, PDFs), compress text-based files

5. Email protocols cổ nhưng không thể thay thế

Aha: SMTP ra đời 1982, trước cả World Wide Web. Nhưng nó vẫn là backbone của email vì:

  • Interoperability: Gmail có thể gửi cho Yahoo, Outlook, self-hosted server
  • Decentralization: không có central authority, bất kỳ ai cũng có thể chạy email server
  • Network effect: tỷ email addresses dùng SMTP → không thể migrate
  • Modern email systems wrap SMTP (Gmail dùng REST API internal, nhưng vẫn speak SMTP ra bên ngoài)

6. “Delivered” không có nghĩa “inbox” — Email deliverability là nghệ thuật

Aha: Receiver’s SMTP server trả 250 OK chỉ có nghĩa “đã accepted vào queue”. Email có thể bị:

  • Đưa vào spam folder (user không thấy)
  • Silently discarded (sau spam filter)
  • Delayed (queued for processing)
  • Bounced sau khi accept (async bounce)

Đây là lý do email deliverability là một lĩnh vực riêng: IP warming, domain reputation, content optimization, feedback loops.

Pitfalls (Bẫy cần tránh)

1. Đừng dùng relational DB cho email storage

Sai: Dùng PostgreSQL/MySQL với SELECT * FROM emails WHERE user_id = ? ORDER BY date DESC LIMIT 50. Đúng ở scale nhỏ (< 10M users), sai ở scale lớn (1B users). 10 trillion rows, sharding RDB phức tạp, join expensive. Dùng wide-column store (Cassandra/Bigtable) với partition key = user_id cho sequential reads.

2. Đừng scan virus sau khi lưu attachment

Sai: Upload attachment → lưu S3 → scan virus async. Đúng: Scan virus trước khi lưu. Nếu lưu trước, file infected có thể bị download trước khi scan xong.

3. Đừng ignore email headers khi implement threading

Sai: Group conversation chỉ dựa trên Subject line. Đúng: Dùng Message-ID + In-Reply-To + References headers (RFC 2822). Subject chỉ là fallback. Nhiều email có cùng subject nhưng không liên quan.

4. Đừng search trực tiếp trên primary database

Sai: Full-text search trên Cassandra/Bigtable. Đúng: Cassandra không có full-text search capability. Dùng dedicated search engine (Elasticsearch). Index async, accept vài giây lag.

5. Đừng quên email size limits ở mọi layer

Sai: Chỉ validate attachment size ở API layer. Đúng: Validate ở mọi layer:

  • SMTP server: SIZE extension (RFC 1870) — reject oversize trước khi nhận DATA
  • API server: multipart upload size limit
  • S3: object size limit
  • Processing pipeline: timeout cho oversized emails

6. Đừng gửi email không có SPF/DKIM/DMARC

Sai: Setup SMTP server và gửi email luôn. Đúng: Không có SPF/DKIM/DMARC → emails sẽ vào spam folder của mọi receiver. Setup cả 3 là prerequisite để email deliverability hoạt động.

7. Đừng underestimate spam filtering resources

Sai: “Spam filter chỉ là 1 service nhỏ.” Đúng: Spam filter phải process 100% incoming traffic (bao gồm 80% spam) → nó cần nhiều resources hơn cả email delivery pipeline. ML inference cho mỗi email, URL scanning, attachment scanning — tất cả phải xong trong vài giây.


Interview Tips — Khi gặp bài này trong phỏng vấn

Câu hỏi thường gặp từ interviewer

Câu hỏiHướng trả lời
”Tại sao không dùng MySQL cho email storage?”Giải thích write-heavy workload, partition key pattern, horizontal scaling của Cassandra
”Làm sao handle 80% spam traffic?”Multi-layer filtering, reject sớm nhất có thể ở connection-level
”Search latency target bao nhiêu?”< 500ms cho full-text, < 100ms cho autocomplete. Dùng Elasticsearch, shard by user_id
”Attachment storage cost quá lớn, optimize thế nào?”Deduplication (SHA-256), storage tiering (hot/warm/cold/archive), CDN cho download
”Làm sao đảm bảo email không bị mất?”Durable queue (Kafka với replication), multi-datacenter storage replication, 11 nines S3 durability
”Threading hoạt động thế nào?”Message-ID + In-Reply-To + References headers (RFC 2822), fallback subject matching
”End-to-end encryption trade-offs?”E2EE → server cannot search, cannot spam filter → client-side only. Trade security vs functionality

Framework khi trả lời

  1. Start with protocols: Show bạn hiểu SMTP, IMAP, POP3 — đây là foundation
  2. Separate 3 flows: Sending, Receiving, Reading — mỗi flow có architecture khác
  3. Emphasize spam early: 80% traffic là spam — nếu không mention → red flag
  4. Storage separation: Metadata vs Body vs Attachments — 3 loại data khác nhau, 3 storage engines
  5. Search as separate system: Elasticsearch/Lucene, async indexing, derived data
  6. Security is not optional: SPF/DKIM/DMARC, TLS, virus scanning — mention ngay từ đầu

Tổng kết

AspectKey DecisionLý do
ProtocolSMTP (send/receive) + REST API (read) + IMAP (backward compat)SMTP là Internet standard, API flexible hơn cho modern clients
Metadata StorageCassandra/BigtableWrite-heavy, partition by user_id, horizontal scaling
Body StorageDistributed Blob StoreCompress với zstd, separate từ metadata cho independent scaling
Attachment StorageS3/GCS với dedup + tiering90% of storage cost, dedup + lifecycle policy tiết kiệm lớn
SearchElasticsearch/LuceneFull-text search, inverted index, shard by user_id
Spam FilterMulti-layer: connection → envelope → header → content (ML)Filter sớm = tiết kiệm resources, ML cho accuracy
QueueKafkaDecouple sending/receiving/processing, handle burst, retry
CacheRedisInbox metadata cache, unread counts, user preferences
SecuritySPF/DKIM/DMARC + TLS + E2EE optionalEmail authentication, encryption in transit + at rest
SyncIMAP IDLE (desktop) + Delta sync (mobile/web)Push for desktop, efficient sync for mobile

“Email system là bài test tổng hợp: distributed storage, search engine, security, queue processing, protocol design — tất cả trong một. Nếu em design được email system tốt, em design được hầu hết mọi thứ.”


Liên kết nội bộ: