- Created migration file for rebaseline of the database schema. - Added tables: auth_clients, tenants, auth_client_keys, webhook_nonces, events_inbox, lists, campaigns, subscriptions, send_jobs, delivery_summary, and send_batches. - Defined relationships and constraints between tables. - Updated DbContext and model snapshot to reflect new entities and their configurations. - Removed deprecated ListMember entity and its references. - Introduced Dockerfile for building and running the SendEngine application. - Enhanced installer program to support tenant creation and webhook client management with Member Center integration.
116 lines
5.2 KiB
Markdown
116 lines
5.2 KiB
Markdown
# Flows
|
||
|
||
本文件描述 Send Engine 的三條核心流程:訂閱事件同步、發送排程、退信處理。
|
||
本引擎只負責事件控制與執行,不提供 UI。
|
||
ESP 介接暫定為 Amazon SES。
|
||
Member Center 為多租戶架構,信件內容由各租戶網站產生後送入 Send Engine。
|
||
|
||
## 1. 訂閱事件同步流程
|
||
目的:以事件為主,建立/更新本地名單快照(tenant/list scope),不做跨租戶查詢。
|
||
|
||
流程:
|
||
1. Member Center 發出事件(`subscription.activated` / `subscription.unsubscribed` / `preferences.updated`)。
|
||
2. Send Engine 的 Event Ingest 接收事件(Webhook 或 Queue 擇一)。
|
||
3. 進行驗證(tenant scope、簽章/Token、重放保護)。
|
||
4. 事件落地為 Inbox(append-only),標記 `received_at`。
|
||
5. Consumer 依 `event_id` 去重並處理:
|
||
- activated → upsert 訂閱者、掛到 tenant/list
|
||
- unsubscribed → 標記狀態為 unsubscribed(保留歷史)
|
||
- preferences.updated → 更新偏好欄位與訂閱狀態
|
||
6. 寫入 List Store 快照(只增量更新,不拉全量)。
|
||
7. 記錄處理結果與版本號(供重播與對帳)。
|
||
|
||
錯誤與重試:
|
||
- 驗證失敗 → 直接拒絕(401/403),不寫入 Inbox。
|
||
- DB 暫時性錯誤 → 重試(含指數退避),仍失敗則進 DLQ。
|
||
- 事件格式錯誤 → 標記為 invalid,記錄原因。
|
||
|
||
## 1b. 全量名單同步流程(由 Member Center 主動推送)
|
||
目的:避免 Send Engine 透過 API 拉取名單,降低名單外流風險。
|
||
|
||
流程:
|
||
1. Member Center 內部管理介面或排程觸發「名單重建」。
|
||
2. Member Center 以 webhook 推送全量名單或分批名單事件(推薦分批)。
|
||
3. Send Engine 驗證來源與 tenant scope。
|
||
4. 事件寫入 Inbox,Consumer 以批次方式重建 List Store。
|
||
5. 重建完成後標記同步版本(sync_version)。
|
||
|
||
注意事項:
|
||
- 建議以批次/游標推送,避免單筆 payload 過大
|
||
- 可於 Send Engine 端提供 `sync_received` 回應與進度回報
|
||
|
||
## 2. 發送排程流程
|
||
目的:從租戶網站送入的內容建立 Send Job,切分送信任務並控速。
|
||
|
||
流程:
|
||
1. 租戶網站以 Member Center 簽發的 token 呼叫 Send Engine API 建立 Campaign/Send Job:
|
||
- 必填:tenant_id、list_id、內容(subject/body/template 其一或組合)
|
||
- 選填:排程時間、發送窗口、追蹤設定(open/click)
|
||
2. Send Engine 驗證 tenant scope 與內容完整性,建立 Send Job。
|
||
- tenant_id 以 token 為準,body 的 tenant_id 僅作一致性檢查
|
||
- tenant 必須預先存在(建議由 Installer 建立)
|
||
- list_id 若不存在,Send Engine 會在該 tenant 下自動建立 list(placeholder)
|
||
3. Scheduler 在排程時間點啟動 Send Job:
|
||
- 讀取 List Store 快照
|
||
- 依規則過濾(已退訂、bounced、黑名單)
|
||
4. 切分成可控批次(batch),寫入 Outbox。
|
||
5. Sender Worker 取出 batch,轉成 SES API 請求。
|
||
6. 發送時必帶:
|
||
- SES Configuration Set
|
||
- Message Tags(至少含 campaign_id / site_id / list_id)
|
||
- Newsletter 類型需帶 `List-Unsubscribe` 與 `List-Unsubscribe-Post` headers
|
||
7. SES 回應 message_id → 記錄 delivery log。
|
||
8. 更新 Send Job 進度(成功/失敗/重試)。
|
||
|
||
控速策略(範例):
|
||
- 全域 TPS 上限 + tenant TPS 上限
|
||
- SES provider-specific rate limit
|
||
- 超限則延後批次並回寫排程
|
||
|
||
內容管理注意事項:
|
||
- Send Engine 不負責內容產生與編輯,僅接收已渲染或可渲染內容
|
||
- 若採模板渲染,模板需由租戶網站提供並由 Send Engine 以 tenant scope 渲染
|
||
|
||
## 3. 退信處理流程
|
||
目的:處理 ESP 回報的 bounce/complaint,並回寫本地名單狀態。
|
||
|
||
流程:
|
||
1. SES 事件由 Configuration Set 發送至 SNS,再落到 SQS。
|
||
2. ECS Worker 輪詢 SQS,解析 SNS envelope 與 SES payload。
|
||
3. 將事件寫入 Inbox(append-only)。
|
||
4. Consumer 解析事件:
|
||
- hard bounce → 立即標記 blacklisted(同義於 `suppressed`)
|
||
- soft bounce → 累計次數,達門檻(預設 5)才標記 blacklisted(`suppressed`)
|
||
- complaint → 立即取消訂閱並標記 blacklisted(`suppressed`)
|
||
- suppression 事件 → 直接對應為 `suppressed`(即黑名單)
|
||
5. 更新 List Store 快照與投遞記錄。
|
||
6. 回寫 Member Center(僅在以下條件):
|
||
- hard bounce:已設黑名單
|
||
- soft bounce:達門檻後設黑名單
|
||
- complaint:取消訂閱
|
||
- suppression:設黑名單
|
||
|
||
補充:
|
||
- Unknown event 不應使 worker crash,應記錄後送入 DLQ
|
||
- Throttle/暫時性網路錯誤使用指數退避重試
|
||
|
||
回寫規則:
|
||
- Send Engine 僅回寫「停用原因」與必要欄位
|
||
- Member Center 需提供可標註來源的欄位(例如 `disabled_by=send_engine`)
|
||
- 回寫原因碼固定為:
|
||
- `hard_bounce`
|
||
- `soft_bounce_threshold`
|
||
- `complaint`
|
||
- `suppression`
|
||
|
||
名詞定義:
|
||
- `blacklisted` 與 `suppressed` 同義,表示此收件者不可再發送
|
||
|
||
資料一致性:
|
||
- 任何狀態改變需保留歷史(append-only events + current snapshot)
|
||
- 避免以 ESP 回報反向新增不存在的訂閱者(僅更新已存在者)
|
||
|
||
多租戶安全性補充:
|
||
- 所有事件與 Send Job 必須攜帶 tenant_id,且不可跨租戶讀寫
|
||
- API 僅允許 tenant scope 的操作(list/read/write)
|