Automation & Reminders
Phần này lo nhắc nhở tự động theo lịch/ngưỡng: hết hạn giấy tờ, hóa đơn đến hạn, lớp sắp đầy chỗ… Khác với notification "tức thời" (phản ứng ngay với một sự việc), reminder được lên lịch trước và đến hạn mới gửi.
📌 Đây là phần lõi của automation pipeline. Reminder là use-case nghiệp vụ đầu tiên; Report Subscription, UserMessage, AutomationJob là các đường mỏng dùng chung toàn bộ hình dạng pipeline.
Bức tranh: ba chặng
| Chặng | Ai làm | Làm gì |
|---|---|---|
| ① Tạo việc | Event Handler + trigger providers | Sự kiện → "lịch việc" trong AutomationScheduler. Không gửi gì. |
| ② Đến hạn | Worker Automation Scheduler | Quét SQL, chốt quyền xử lý từng row (lease + Version), giao việc gửi sang NotificationScheduler. |
| ③ Giao hàng | Pipeline notification | Render + gửi (xem Kiến trúc Communication). |
⚠️ "Automation hoàn thành" = đã xếp việc giao hàng, không phải đã tới tay người nhận.
AutomationScheduler — nguồn sự thật "việc gì đến hạn"
Bảng SQL điều phối due-work. Worker chỉ poll SQL; AutomationJob (Azure Table) là bản sao trace mỏng, không bao giờ dùng để quét việc.
Cột tiêu biểu (rút gọn):
AutomationTypeId -- Reminder | ReportSubscription
TriggerTypeId -- DateBased | ThresholdCondition | Recurring | ScheduledRun | ManualRun
SchedulerKey -- khóa nghiệp vụ tất định (UNIQUE)
ScheduledOn -- UTC; (ScheduledLocalDate + TimeZoneId giữ lịch local của BU)
StatusId -- Pending | Processing | Completed | Failed | Deactivated | Cancelled
ProcessingLeaseExpiresOn -- hạn lease chống crash, KHÔNG phải danh tính worker
RetryCount / NextRetryOn / FailureReasonCode / IsOnSupportHold
InputHash -- sha256 input chuẩn hóa + settings ảnh hưởng khóa
DispatchResultJson -- tọa độ downstream + tóm tắt kết quả (nhỏ)
Version -- concurrency stamp (chống hai worker giẫm chân)Row coordinator phải gọn: không nhét input lớn, recipient snapshot, output render hay diagnostics sâu vào SQL — phần đó để ở
TraceBlobName(blob) khi support cần.
SchedulerKey — khóa idempotency
Cùng dạng triết lý "khóa định danh" của toàn hệ thống:
reminder:{settingGuid}:{sourceType}:{sourceGuid}:{triggerKey}:{targetDate}:{offsetDays}
reminder:{settingGuid}:{sourceType}:{sourceGuid}:{triggerKey}:threshold:{thresholdKey}
report-subscription:{guid}:scheduled:{periodStartUtc-or-scheduledOn}
report-subscription:{guid}:manual:{manualRunGuid}Event, retry, reconciliation đều cho cùng khóa. Không chứa GUID ngẫu nhiên (trừ manualRunGuid đã sinh trước), text hiển thị, danh sách recipient hay template.
Ensure semantics
| Tình huống | Hành vi |
|---|---|
| Chưa có row | Insert. |
Row tương thích ở Pending/Failed/Deactivated/Cancelled | Update/reactivate, giữ diagnostics + attempts + tọa độ dispatch cũ. |
Row Completed | Trả tọa độ, không gửi lại (muốn gửi thật lần nữa cần trigger key mới hoặc support-retry key). |
Cùng khóa, InputHash lệch | Support hold / conflict, không bao giờ ghi đè. |
Processing — Lease + Version (chống giẫm chân)
1. Query Pending, ScheduledOn <= now, NextRetryOn null/<=now, IsOnSupportHold=false
2. Claim: Status=Processing, lease = now + N WHERE Status=Pending AND Version=read.Version
3. Chỉ xử lý nếu đúng 1 row đổi; giữ Version mới làm token
4. Complete/Fail/Deactivate WHERE Version=token- Khóa chống artifact trùng; Version chống ghi đè state mới bởi worker cũ (sau khi lease hết và có claim mới hơn).
- Stale cleanup:
Processing+ lease quá hạn →Pending,RetryCount += 1, backoff. - Backoff: 5 phút → 15 phút → 1 giờ →
IsOnSupportHold(chỉ handler dài hạn được duyệt mới retry hằng ngày). - Lỗi do source/settings không hợp lệ lúc đến hạn →
Deactivated/Skipped, không phảiFailed.
Reminders — settings & trigger providers
Event handler ensure/deactivate/recreate các instance khi dữ liệu nguồn đổi. Chúng không gửi, không render, không quét bảng domain rộng.
Luồng chính
parse event → load state nguồn → resolve BU timezone → load ReminderSetting + NotificationSettings
→ DEACTIVATE row cũ trước → ensure AutomationScheduler rows → (ensure immediate notification nếu cần) → metricsProvider contracts (worker route tới provider, không phải một switch khổng lồ)
| Interface | Vai trò |
|---|---|
IReminderTriggerProvider | Build scheduler candidate (chỉ tạo lịch, không resolve recipient). |
IEventInvalidationProvider | Deactivate row không còn hợp lệ. |
IImmediateNotificationProvider | Tạo notification tức thời khi cần. |
IReminderRecipientProvider / IReminderDeliveryPolicy | Resolve người nhận / kênh lúc execution (nên pending row giữ ổn định khi config đổi). |
IEventHandlerđăng ký trên seam post-processor (mộtIEntityEventPostProcessorvới registry riêng —WKR-009), không phảiHandlerswitch. Xem Platform.
Hai kiểu trigger
| Kiểu | Cách | Ví dụ |
|---|---|---|
| DateBased | scheduledLocalDate = targetDate − offsetDays; đổi sang UTC theo timezone BU (DST theo product rule). | Police vetting/first-aid hết hạn, work anniversary, invoice reminder. |
| ThresholdCondition | Event cập nhật aggregate → tính metric → so ngưỡng → đạt thì ensure row, hết đạt thì deactivate. | Booking capacity sắp đầy. |
thresholdKey = {metricKey}:{thresholdValue}:{firingPolicy}:{scopeKey} — firing policy (OncePerSource | OncePerThreshold | OncePerDay | FalseToTrue) là một phần của khóa.
Khi settings đổi
| Thay đổi | Hành vi |
|---|---|
| Offset/date/threshold | Deactivate + recreate (occurrence identity đổi). |
| Send-time | Update tại chỗ (cùng SchedulerKey, ScheduledOn mới). |
| Template/delivery | Pending row giữ nguyên (execution reload config). |
| Setting deactivated | Deactivate pending rows. Completed rows luôn giữ cho history. |
Execution-time revalidation
Đến hạn, handler reload setting/source/recipients/template, so với InputHash. Nguồn/setting không còn hợp lệ → Deactivated (ReasonCode SourceNoLongerValid/ReminderSettingInactive) — đây là outcome, không phải failure.
Reconciliation có biên (không outbox)
Không có AutomationEventOutbox. Source event là chính; mỗi trigger provider khai một đường quét-sửa có giới hạn: source filter (chỉ active), watermark "changed-since", due window (today..today+N), allowlist BU/org, batch size, cursor; chạy tần suất thấp; chỉ advance watermark khi batch thành công; dùng cùng key helper với đường event.
Cấm: full-domain scan thường xuyên, unbounded historical scan, hay reconciliation sinh khác khóa.
Liên quan
- Report Subscription — đường automation mỏng cho báo cáo định kỳ.
- Kiến trúc Communication — pipeline gửi & key policy.
- Platform — seam post-processor & EntityEvent.