n8n chạy lỗi giữa chừng: retry thế nào để không tạo dữ liệu trùng?
Thiết kế workflow n8n có thể retry an toàn bằng idempotency key, unique constraint, trạng thái xử lý và quy tắc chỉ retry lỗi tạm thời.

Một workflow n8n có thể tạo khách hàng ở CRM thành công, sau đó lỗi khi gửi email hoặc cập nhật bảng tính. Nếu chạy lại toàn bộ execution, bước đầu có thể tạo thêm một khách hàng, một đơn hàng hoặc một giao dịch giống hệt. Cách đúng không phải là tắt retry, mà là làm cho các bước có tác động ra bên ngoài trở nên idempotent: cùng một yêu cầu được xử lý nhiều lần nhưng kết quả nghiệp vụ chỉ phát sinh một lần.
Mẫu triển khai thực tế gồm bốn lớp: tạo một idempotency_key ổn định từ sự kiện gốc; lưu trạng thái xử lý trong kho dữ liệu dùng chung; bảo vệ bằng unique constraint hoặc conditional write; và chỉ retry những lỗi tạm thời với giới hạn rõ ràng. Khi nhận lại cùng khóa, workflow trả kết quả đã có hoặc tiếp tục từ trạng thái an toàn, thay vì tạo bản ghi mới.
Nguyên nhân
Vì sao chạy lại execution dễ tạo dữ liệu trùng?
Workflow đã thất bại không có nghĩa mọi bước trước đó đều được hoàn tác.
n8n điều phối nhiều hệ thống độc lập nên không có một transaction chung bao phủ CRM, email, ERP và bảng tính. Một API có thể đã ghi dữ liệu nhưng response bị timeout; n8n chỉ nhìn thấy lỗi và không biết tác vụ đã hoàn tất hay chưa. Đây là trạng thái kết quả không chắc chắn, nguy hiểm hơn một lỗi rõ ràng trước khi ghi dữ liệu.
Rủi ro thường xuất hiện ở các node tạo mới, gửi thông báo, trừ tồn kho, phát hành hóa đơn hoặc kích hoạt workflow khác. Retry On Fail hữu ích với lỗi tạm thời, nhưng tự nó không chống trùng. Việc chạy lại execution thủ công cũng có thể thực thi lại những node đã thành công. Vì vậy, ranh giới chống trùng phải nằm tại thao tác nghiệp vụ hoặc ngay trước thao tác đó, không chỉ ở cơ chế retry của n8n.
Quy tắc cốt lõi
Bốn lớp bảo vệ trước khi bật retry
Mỗi lớp xử lý một kiểu lỗi khác nhau; chỉ kiểm tra tồn tại bằng một node IF là chưa đủ khi có concurrency.
Khóa ổn định
Dùng ID của sự kiện nguồn hoặc khóa nghiệp vụ; retry phải tái sử dụng đúng khóa cũ.
Ràng buộc nguyên tử
Unique index, INSERT ON CONFLICT hoặc conditional write ngăn hai execution cùng tạo dữ liệu.
Máy trạng thái
Theo dõi RECEIVED, PROCESSING, SUCCEEDED, RETRYABLE_FAILED và PERMANENT_FAILED.
Retry có chọn lọc
Retry timeout, 429 và một số 5xx; fail fast với dữ liệu sai, thiếu quyền và lỗi nghiệp vụ.

Thiết kế
Tạo idempotency key đúng cho từng sự kiện
Khóa phải biểu diễn một ý định nghiệp vụ, không phải một lần chạy n8n.
Ưu tiên khóa do hệ thống nguồn cung cấp, chẳng hạn order_id, payment_event_id, message_id hoặc form_submission_id. Nếu nguồn không có ID đáng tin cậy, tạo khóa từ các trường bất biến đã chuẩn hóa, ví dụ tenant_id + event_type + external_reference, rồi băm bằng SHA-256. Không dùng n8n execution ID vì mỗi lần chạy lại có ID khác; cũng không nên chỉ dùng timestamp vì cùng một nghiệp vụ có thể đến vào thời điểm khác nhau.
Phạm vi khóa phải đủ chặt. customer_id đơn lẻ sẽ chặn cả những đơn hợp lệ tiếp theo của cùng khách hàng; ngược lại, một random UUID mới ở mỗi lần retry sẽ không nhận diện được bản sao. Nên lưu thêm payload_hash. Nếu cùng khóa nhưng payload khác, dừng với lỗi xung đột để tránh âm thầm áp dụng dữ liệu mới vào kết quả cũ. Đặt thời gian lưu khóa theo vòng đời nghiệp vụ: webhook thanh toán có thể cần lâu hơn một tác vụ đồng bộ ngắn hạn.
Triển khai n8n
Mẫu workflow retry an toàn từng bước
Dùng kho trạng thái dùng chung để nhiều worker và nhiều execution cùng nhìn thấy một sự thật.
- Nhận sự kiện và chuẩn hóa: xác thực webhook, chuẩn hóa các trường dùng tạo khóa và giữ nguyên correlation ID từ đầu đến cuối.
- Đăng ký yêu cầu: ghi
idempotency_key,payload_hash,status=PROCESSING,locked_untilvà thời điểm nhận bằng một thao tác nguyên tử. Trong PostgreSQL, dùng unique index trên khóa vàINSERT ... ON CONFLICT; trong API đích, truyền header idempotency nếu dịch vụ hỗ trợ. - Phân nhánh theo kết quả: nếu tạo mới thành công, tiếp tục. Nếu khóa đã
SUCCEEDED, trả response hoặc reference đã lưu. Nếu đangPROCESSINGvà lease còn hạn, kết thúc như duplicate in-flight. Nếu lease hết hạn, chỉ một execution được quyền nhận lại khóa bằng conditional update. - Thực hiện side effect: truyền cùng idempotency key xuống dịch vụ đích. Với hệ thống không hỗ trợ, dùng bảng ánh xạ khóa sang external ID và unique constraint tại lớp tích hợp.
- Chốt kết quả: lưu
SUCCEEDED, external ID và response tối thiểu cần trả lại. Nếu lỗi tạm thời, lưuRETRYABLE_FAILED; nếu lỗi dữ liệu hoặc quyền, lưuPERMANENT_FAILED. - Phản hồi: webhook đồng bộ chỉ trả thành công sau khi trạng thái cần thiết đã được chốt. Với tác vụ dài, phản hồi đã tiếp nhận rồi xử lý bất đồng bộ qua queue/outbox.
Không dùng mô hình “SELECT xem có chưa, rồi INSERT” nếu không có transaction hoặc unique constraint. Hai execution chạy đồng thời đều có thể thấy chưa tồn tại và cùng tạo bản ghi.
Retry policy
Lỗi nào nên retry và lỗi nào phải dừng?
Retry sai loại lỗi có thể khuếch đại sự cố và làm đầy hàng đợi.
Chỉ retry khi lỗi có khả năng tự hết: timeout mạng, mất kết nối ngắn, HTTP 429, hoặc 502/503/504. Thiết lập số lần thử tối đa, thời gian chờ tăng dần và một khoảng ngẫu nhiên nhỏ để nhiều execution không gọi lại cùng lúc. Tôn trọng Retry-After khi API trả về. Tổng thời gian retry phải nhỏ hơn thời hạn nghiệp vụ và không vượt quá khả năng xử lý của hệ thống đích.
Không retry tự động với 400/422 do payload sai, 401/403 do xác thực hoặc phân quyền, 404 mang ý nghĩa nghiệp vụ, hay các validation failure. Các lỗi này cần fail fast, ghi lý do rõ ràng và đưa vào hàng chờ xử lý thủ công nếu cần. Với timeout sau thao tác tạo mới, trước khi thử tạo lại hãy truy vấn theo idempotency key hoặc external reference để xác định kết quả cũ.
Trong n8n, có thể bật Retry On Fail ở node phù hợp và dùng Error Workflow để cảnh báo hoặc điều phối xử lý lỗi. Tuy nhiên, Error Workflow không thay thế kho idempotency. Đừng để cả node, reverse proxy, queue và workflow cha cùng retry không kiểm soát; hãy chọn một tầng chịu trách nhiệm chính và tính tổng số lần gọi tệ nhất.
Vận hành
Kiểm thử và giám sát trước khi đưa vào production
Chứng minh workflow chống trùng bằng failure injection, không chỉ bằng happy path.
Tối thiểu hãy kiểm thử sáu tình huống: gửi cùng payload hai lần tuần tự; gửi đồng thời từ hai execution; timeout sau khi hệ thống đích đã ghi; lỗi trước khi ghi; worker dừng khi đang giữ lease; và cùng khóa nhưng payload khác. Tiêu chí đạt là chỉ có một side effect, các lần gọi lại nhận cùng external reference, lease hết hạn có thể phục hồi, còn payload xung đột bị chặn.
Theo dõi các chỉ số như số duplicate bị chặn, số retry theo mã lỗi, thời gian ở trạng thái PROCESSING, lease được thu hồi, permanent failure, độ sâu hàng đợi và tỷ lệ yêu cầu cần xử lý thủ công. Log phải chứa correlation ID, idempotency key đã che bớt nếu nhạy cảm, workflow/execution ID, attempt, trạng thái cũ-mới và external reference. Không ghi token, mật khẩu hoặc toàn bộ payload chứa dữ liệu cá nhân.
Checklist trước khi phát hành: mọi node có side effect đã được liệt kê; khóa và phạm vi khóa được tài liệu hóa; unique constraint đã tồn tại; trạng thái và timeout rõ ràng; retry budget có giới hạn; có dead-letter/manual review; dashboard và alert đã cấu hình; runbook mô tả cách replay mà không đổi khóa.
Câu hỏi thường gặp
FAQ về retry và dữ liệu trùng trong n8n
Các quyết định thường gặp khi chuyển workflow từ thử nghiệm sang vận hành ổn định.
Bật Retry On Fail trong n8n có đủ chống dữ liệu trùng không?
Không. Tính năng này chỉ thực hiện lại node khi lỗi. Side effect phải được bảo vệ bằng idempotency key và thao tác nguyên tử ở database hoặc API đích.
Có thể dùng execution ID của n8n làm idempotency key không?
Không nên cho retry toàn workflow vì lần chạy mới có execution ID mới. Hãy dùng ID của sự kiện nguồn hoặc khóa nghiệp vụ ổn định qua mọi lần thử.
Dùng node IF để kiểm tra bản ghi tồn tại có đủ không?
Không đủ khi có hai execution đồng thời. Cần unique constraint, upsert hoặc conditional write để bước kiểm tra và ghi được bảo vệ nguyên tử.
Nếu API đã tạo dữ liệu nhưng n8n bị timeout thì làm gì?
Không tạo lại ngay. Tra cứu bằng idempotency key hoặc external reference. Nếu tìm thấy, lưu kết quả đó và chuyển trạng thái sang SUCCEEDED; chỉ thử tạo lại khi có bằng chứng thao tác cũ chưa xảy ra.
Nên lưu idempotency key bao lâu?
Theo khoảng thời gian mà cùng nghiệp vụ có thể được gửi lại và mức độ nghiêm trọng của việc trùng. Quy định TTL theo loại sự kiện, nhưng giữ dấu vết lâu hơn cho thanh toán, hóa đơn và đơn hàng nếu yêu cầu kiểm toán cần.
FlowNexa
Muốn workflow n8n retry được mà vẫn an toàn dữ liệu?
FlowNexa có thể cùng doanh nghiệp rà soát các side effect, thiết kế khóa chống trùng, retry policy, logging và kịch bản phục hồi phù hợp với hệ thống hiện tại.
Bắt đầu từ workflow rủi ro cao
Ưu tiên đơn hàng, thanh toán, CRM, email và các luồng có nhiều hệ thống đích.
Đo bằng tiêu chí kiểm thử
Xác nhận bằng concurrent replay và timeout injection trước khi bật retry trên production.



