178 lines
8.1 KiB
Markdown
178 lines
8.1 KiB
Markdown
# PART 2 — biến thể `batch-job`
|
||
|
||
> **Dùng khi** `PRODUCT = batch-job` — job chạy theo lịch, không giao diện: đối soát đêm,
|
||
> sinh báo cáo định kỳ, dọn dữ liệu, gửi thông báo hàng loạt. Cắm khối này vào chỗ PART 2
|
||
> của [`../srs.md`](../srs.md).
|
||
>
|
||
> **Tiêu chí G3 riêng của biến thể này**: mỗi job có lịch + cửa sổ + phụ thuộc · quy tắc xử
|
||
> lý từng bản ghi đầy đủ · **đã trả lời xong idempotency và thất bại giữa chừng** · có kế
|
||
> hoạch cảnh báo và người trực.
|
||
|
||
**Phân biệt với `data-pipeline`:** pipeline có sản phẩm là **dữ liệu để phân tích** (cần
|
||
data contract, lineage, đối soát nguồn–đích); batch-job có sản phẩm là **việc được làm xong**
|
||
(cần idempotency, checkpoint, cảnh báo). Job vừa di chuyển dữ liệu lớn vừa làm việc nghiệp
|
||
vụ ⇒ nạp cả hai biến thể.
|
||
|
||
---
|
||
|
||
## 2.1 Danh sách job
|
||
|
||
| ID | Job | Làm gì | Lịch | Cửa sổ cho phép | Phụ thuộc job nào | Khối lượng/lần |
|
||
|---|---|---|---|---|---|---|
|
||
| JOB-01 | | | Hằng ngày 01:00 | 01:00–05:00 | JOB-00 xong | ~… bản ghi |
|
||
|
||
**Cột `Cửa sổ cho phép`** — job phải xong trước mấy giờ, và vì sao (nghiệp vụ nào bắt đầu
|
||
lúc đó). Không có cột này thì không ai biết chạy chậm bao lâu là sự cố.
|
||
|
||
## 2.2 Sơ đồ phụ thuộc
|
||
|
||
<!-- archify: workflow · diagrams/SRS_<US>_phu-thuoc-job.workflow.json -->
|
||
```mermaid
|
||
flowchart LR
|
||
J00["JOB-00 · 02:00 Nạp dữ liệu"]
|
||
J01["JOB-01 · 02:30 Đối soát"]
|
||
J02["JOB-02 · 03:00 Gửi báo cáo"]
|
||
STOP(["⛔ Dừng chuỗi · cảnh báo trực đêm"])
|
||
|
||
J00 -->|xong| J01
|
||
J01 -->|xong| J02
|
||
J00 -->|thất bại| STOP
|
||
J01 -->|thất bại| STOP
|
||
```
|
||
|
||
*Nhãn node mang **giờ chạy**; ID khớp cột `ID` bảng §2.1.*
|
||
|
||
🔴 **Vẽ cả cạnh thất bại.** Sơ đồ chỉ có đường "xong" bỏ sót đúng câu hỏi quan trọng nhất:
|
||
job trước hỏng thì job sau **chạy hay dừng**.
|
||
|
||
**Bảng đi kèm** *(quy tắc W13)*:
|
||
|
||
| Job | Phụ thuộc | Job trước thất bại ⇒ | Chạy với dữ liệu cũ có nguy hiểm không | Ai được báo |
|
||
|---|---|---|---|---|
|
||
| JOB-01 | JOB-00 | Dừng, không chạy | 🔴 Có — đối soát trên dữ liệu thiếu ra kết quả sai | Trực đêm |
|
||
| JOB-02 | JOB-01 | Dừng, không gửi báo cáo | 🔴 Có — gửi báo cáo sai còn tệ hơn không gửi | Trực đêm + kế toán |
|
||
|
||
Cột áp chót là cột quyết định: dữ liệu cũ vô hại ⇒ cho chạy tiếp; nguy hiểm ⇒ phải dừng.
|
||
Không trả lời được ⇒ `OQ`, đừng mặc định cho chạy.
|
||
|
||
---
|
||
|
||
## 2.3 JOB-01 — <Tên job>
|
||
|
||
### 2.3.1 Định danh
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Kích hoạt bởi** | Lịch / Sự kiện / Gọi tay |
|
||
| **Đầu vào** | *(bảng, file, hàng đợi — và phạm vi: ngày nào, trạng thái nào)* |
|
||
| **Đầu ra** | *(bản ghi được cập nhật, file sinh ra, thông báo gửi đi)* |
|
||
| **Thời gian chạy dự kiến** | ~… phút với khối lượng bình thường |
|
||
| **Chạy bao lâu thì coi là treo** | |
|
||
| **Có thể chạy đồng thời nhiều bản không** | 🔴 Không ⇒ cơ chế khoá là gì |
|
||
|
||
### 2.3.2 Phạm vi xử lý
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Chọn bản ghi nào để xử lý** | *(điều kiện chính xác)* |
|
||
| **Bản ghi đã xử lý rồi nhận biết bằng gì** | 🔴 *(cột trạng thái? bảng log? mốc thời gian?)* |
|
||
| **Thứ tự xử lý có quan trọng không** | |
|
||
| **Giới hạn số bản ghi mỗi lần chạy** | *(và phần còn lại xử lý khi nào)* |
|
||
|
||
### 2.3.3 Quy tắc xử lý từng bản ghi
|
||
|
||
*Đây là phần thay thế cho "bảng field" của biến thể `screen` — phải cụ thể ngang vậy.*
|
||
|
||
| # | Điều kiện | Hành động | Kết quả ghi vào đâu | BR |
|
||
|---|---|---|---|---|
|
||
| 1 | | | | BR-0nn |
|
||
|
||
**Bản ghi lỗi giữa chừng:**
|
||
|
||
| Tình huống | Xử lý | Ghi log gì |
|
||
|---|---|---|
|
||
| Dữ liệu bản ghi không hợp lệ | Bỏ qua và tiếp tục / Dừng cả job | |
|
||
| Gọi hệ thống ngoài thất bại | Retry mấy lần? Rồi sao? | |
|
||
|
||
🔴 **"Bỏ qua và tiếp tục" phải kèm ngưỡng.** Bỏ qua 3 bản ghi là bình thường; bỏ qua 30% số
|
||
bản ghi là sự cố nhưng job vẫn báo "thành công" — đó là chế độ hỏng im lặng.
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Ngưỡng tỷ lệ lỗi để job tự đánh dấu thất bại** | > …% |
|
||
|
||
### 2.3.4 Idempotency — chạy lại
|
||
|
||
🔴 **Câu hỏi bắt buộc, không được để trống:**
|
||
|
||
| Câu hỏi | Trả lời |
|
||
|---|---|
|
||
| Chạy lại cùng một ngày hai lần ⇒ kết quả có giống lần đầu không? | ✅/❌ |
|
||
| Nếu ❌: hậu quả cụ thể là gì | *(gửi email hai lần? cộng tiền hai lần?)* |
|
||
| Nếu ❌: quy trình dọn trước khi chạy lại | |
|
||
| Ai được phép chạy lại | |
|
||
| Chạy lại có cần khoảng thời gian cụ thể không | |
|
||
|
||
Job **gửi thông báo, ghi bút toán, gọi API bên ngoài** mà không idempotent là rủi ro nghiêm
|
||
trọng — nêu rõ trong `RISK` chứ không chỉ ghi ở đây.
|
||
|
||
### 2.3.5 Thất bại giữa chừng
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Có checkpoint không** | ✅/❌ — lưu ở đâu, mức nào (mỗi bản ghi? mỗi lô?) |
|
||
| **Chạy lại tiếp tục từ checkpoint hay từ đầu** | |
|
||
| **Có rollback không** | Toàn bộ / Không có / Từng lô |
|
||
| **Trạng thái dở dang có làm hỏng nghiệp vụ khác không** | 🔴 *(job sau đọc dữ liệu chưa xong)* |
|
||
| **Cách nhận biết job đang chạy dở vs. đã xong** | |
|
||
|
||
### 2.3.6 Chạy tay
|
||
|
||
| | |
|
||
|---|---|
|
||
| Ai được chạy tay | |
|
||
| Tham số truyền được | *(ngày nào, phạm vi nào)* |
|
||
| Có cần phê duyệt không | *(bắt buộc nếu `RIGOR = strict`)* |
|
||
| Chạy tay có ghi vết khác chạy tự động không | |
|
||
|
||
## 2.4 Trạng thái tương đương "màn hình rỗng"
|
||
|
||
| Tình huống | Job làm gì | Coi là thành công? |
|
||
|---|---|---|
|
||
| Không có bản ghi nào thoả điều kiện | Kết thúc, ghi log "0 bản ghi" | ✅ Có — **không phải lỗi** |
|
||
| Đầu vào chưa sẵn sàng (job trước chưa xong) | Chờ / Bỏ qua lần này / Báo lỗi | |
|
||
| Chạy đúng lịch nhưng hôm đó nghỉ lễ | | |
|
||
|
||
🔴 Phân biệt **"không có gì để làm"** với **"không lấy được dữ liệu"**. Cả hai đều ra 0 bản
|
||
ghi nhưng ý nghĩa ngược nhau, và gộp chúng làm sự cố bị bỏ qua nhiều ngày.
|
||
|
||
## 2.5 Cảnh báo và vận hành
|
||
|
||
| Sự kiện | Mức | Báo cho ai | Qua kênh nào | Trong bao lâu |
|
||
|---|---|---|---|---|
|
||
| Job thất bại | 🔴 | | | Ngay |
|
||
| Job chạy quá cửa sổ cho phép | 🔴 | | | Ngay |
|
||
| Tỷ lệ bản ghi lỗi vượt ngưỡng | 🟠 | | | Ngay |
|
||
| **Job không chạy** *(lịch không kích hoạt)* | 🔴 | | | Sau … phút quá giờ |
|
||
|
||
🔴 **Dòng cuối là dòng hay bị quên nhất.** Job thất bại thì có cảnh báo; job *không chạy*
|
||
thì im lặng hoàn toàn — không có gì để báo lỗi. Phải có cơ chế phát hiện "đến giờ mà chưa
|
||
thấy job nào bắt đầu".
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Ai trực ban đêm** | |
|
||
| **Sổ tay xử lý sự cố ở đâu** | *(đây là `MANUAL` của GĐ5 với loại sản phẩm này)* |
|
||
| **Hỏng bao lâu thì phải báo người dùng nghiệp vụ** | |
|
||
|
||
## 2.6 Nghiệm thu — thay cho UAT thông thường
|
||
|
||
| # | Cách kiểm | Tiêu chí đi tiếp |
|
||
|---|---|---|
|
||
| 1 | Chạy trên dữ liệu sao chép từ môi trường thật | Kết quả khớp với xử lý tay trên mẫu … bản ghi |
|
||
| 2 | **Chạy song song với cách làm cũ** ≥ 1 chu kỳ nghiệp vụ | Sai lệch = 0, hoặc mọi sai lệch giải thích được |
|
||
| 3 | Thử chạy lại | Kết quả không đổi (nếu idempotent) |
|
||
| 4 | Thử ngắt giữa chừng rồi chạy lại | Không mất, không trùng bản ghi |
|
||
|
||
Bước 4 hay bị bỏ, và nó là bước duy nhất chứng minh §2.3.5 hoạt động thật.
|