Xử lý lỗi và retry
Bạn sẽ học được
- Cấu hình retry hợp lý cho từng loại lỗi.
- Phân biệt lỗi tạm thời và lỗi vĩnh viễn.
- Dùng timeout để tránh task treo.
- Gắn callback cảnh báo khi thất bại.
- Hiểu vì sao retry có thể làm hỏng dữ liệu.
Khái niệm cốt lõi
Task có thể thất bại vì nhiều lý do:
| Loại | Ví dụ | Retry có ích? |
|---|---|---|
| Tạm thời | Mạng chập, API quá tải | Có |
| Tài nguyên | Hết bộ nhớ, hết dung lượng | Có, nếu đã xử lý nguyên nhân |
| Dữ liệu | Nguồn rỗng, schema đổi | Không |
| Logic | Bug trong code | Không |
| Quyền | Sai thông tin đăng nhập | Không |
Retry mù quáng biến lỗi nhanh thành lỗi chậm, và với task không idempotent, biến lỗi thành sai dữ liệu.
Cấu hình cơ bản
from datetime import timedelta
default_args = {
"retries": 3,
"retry_delay": timedelta(minutes=5),
"retry_exponential_backoff": True,
"max_retry_delay": timedelta(minutes=60),
"execution_timeout": timedelta(hours=2),
}retries=3: thử tối đa ba lần sau lần đầu.retry_delay: chờ 5 phút giữa các lần.retry_exponential_backoff: 5, 10, 20 phút — tránh dồn dập vào API đang quá tải.execution_timeout: dừng task treo, tránh chiếm slot vô hạn.
Timeout quan trọng hơn retry
Một task treo không bao giờ kết thúc sẽ chiếm worker mãi mãi. Không có timeout, retry không bao giờ được kích hoạt vì task chưa thất bại.
Đặt timeout theo thời gian chạy bình thường:
Chạy bình thường: 12 phút
Timeout hợp lý: 60 phútĐừng đặt timeout sát thời gian bình thường — một ngày dữ liệu lớn hơn sẽ bị cắt oan.
Trạng thái task
| Trạng thái | Nghĩa |
|---|---|
success | Thành công |
failed | Thất bại sau khi hết retry |
upstream_failed | Task trước thất bại, task này không chạy |
skipped | Bị bỏ qua theo điều kiện |
up_for_retry | Đang chờ thử lại |
Khi debug, xem trạng thái từng task, không chỉ trạng thái DAG run. Một DAG run failed thường chỉ có một task gốc hỏng.
Callback
def on_failure(context):
dag_id = context["dag"].dag_id
task_id = context["task_instance"].task_id
logical_date = context["logical_date"]
print(f"LỖI {dag_id}.{task_id} tại {logical_date}")
@dag(
dag_id="daily_orders",
default_args={"on_failure_callback": on_failure, "retries": 2},
...
)Callback chạy sau khi hết retry. Đừng gửi cảnh báo ở mỗi lần retry — sẽ tạo nhiễu.
Với thông báo quan trọng, callback nên idempotent: kiểm tra đã gửi chưa trước khi gửi lại.
SLA
@task(sla=timedelta(hours=4))
def load():
...SLA cảnh báo khi task chạy lâu hơn dự kiến, kể cả khi cuối cùng vẫn thành công. Hữu ích cho pipeline có cam kết thời gian với người dùng cuối.
SLA không dừng task — nó chỉ gửi cảnh báo.
Retry và idempotency
Đây là điểm nguy hiểm nhất.
Task INSERT 12.000 dòng, lần đầu ghi xong nhưng lỗi khi cập nhật metadata. Retry chạy lại và ghi thêm 12.000 dòng. Kết quả: 24.000 dòng, không có lỗi nào.
Quy tắc: chỉ bật retry tự động cho task đã idempotent, hoặc cho task chỉ đọc và tính toán.
Với task ghi dữ liệu, sửa sang xoá partition hoặc MERGE trước khi bật retry.
Cảnh báo khi không có task nào chạy
Một dạng lỗi khó thấy: DAG không được lập lịch, hoặc scheduler dừng. Không có task thất bại, nên không có cảnh báo.
Cần thêm giám sát độ tươi dữ liệu: nếu bảng không có dữ liệu mới trong 26 giờ, cảnh báo. Bài 7 nói kỹ hơn.
Lỗi thường gặp
- Retry cho task không idempotent: dữ liệu trùng.
- Không đặt timeout: task treo chiếm worker.
- Cảnh báo mỗi lần retry: nhiễu, mất phản ứng.
- Retry lỗi dữ liệu: thử lại cũng thất bại, chỉ tốn thời gian.
- Không xem trạng thái task: debug sai chỗ.
- Không giám sát DAG im lặng: không biết pipeline đã dừng.
Thực hành
- Đặt
retries=3với exponential backoff. - Thêm
execution_timeoutcho một task. - Viết callback in ra DAG, task và logical date.
- Giải thích vì sao retry có thể gây trùng dữ liệu.
- Thiết kế cảnh báo cho trường hợp DAG không chạy.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
| Dữ liệu trùng sau lỗi | Retry task không idempotent | Sửa cách ghi |
| Task treo mãi | Không có timeout | execution_timeout |
| Quá nhiều cảnh báo | Callback ở mỗi retry | Chỉ cảnh báo sau cùng |
| Retry vô ích | Lỗi logic hoặc dữ liệu | Sửa gốc, giảm retry |
| Không biết pipeline dừng | Không giám sát độ tươi | Thêm kiểm tra freshness |
Tóm tắt
- Chỉ retry lỗi tạm thời.
- Timeout bảo vệ worker khỏi task treo.
- Cảnh báo sau khi hết retry, không phải mỗi lần.
- Retry chỉ an toàn với task idempotent.
- Giám sát cả trường hợp không có task nào chạy.
Use ← → to move between lessons