Skip to content

ADR-0001. Tạo sổ bất đồng bộ do Kafka điều khiển qua một topic self-loop

TrườngGiá trị
StatusAccepted
Date2026-03-30
Decidersledger-team
Supersedes

Bối cảnh

  • Tạo một sổ là chậm và bùng nổ: lấy dữ liệu + render PDF Typst + render XLSX ExcelJS + mã hoá AES + upload S3, dễ tới vài giây mỗi tài liệu, nhân lên trên một batch cả năm.
  • Một request HTTP đồng bộ không thể giữ mở lâu vậy, và một request bị crash sẽ để lại file S3 dang dở và trạng thái mơ hồ.
  • Việc tạo phải retry được và sống sót qua crash của worker giữa pipeline.

Quyết định

Chúng ta sẽ tách enqueue khỏi execution bằng một topic Kafka ledger.generate mà service vừa produce tới (role api) vừa consume từ (role worker). Request HTTP trả về ngay với một LedgerJobPENDING; worker thực thi handleGeneration(ledgerId) và báo tiến độ qua WebSocket.

Consumer chạy với autocommit: false và commit chỉ sau khi upload + finalize thành công. Trạng thái job là một máy LedgerJob riêng (PENDING → PROCESSING → COMPLETED|REJECTED) được claim qua UPDATE có điều kiện nguyên tử.

Hệ quả

ƯuNhược
Response HTTP nhanh; render lâu nằm ngoài đường requestNhất quán cuối cùng — client phải poll/subscribe trạng thái
Enqueue idempotent trên (merchantId, type, period)Một message đã commit không bao giờ auto-replay; recovery cần retry rõ ràng hoặc quét kẹt
Scale ngang qua số consumer / replica workerNgười vận hành phải hiểu self-loop (không có producer/consumer ngoài)
Phục hồi crash qua RecoveryComponent re-enqueue job kẹtHơi nhiều bộ phận chuyển động hơn một queue BullMQ

Phương án đã cân nhắc

Phương ánƯuNhượcVì sao loại
Tạo HTTP đồng bộĐơn giản nhấtKết nối giữ lâu, không phục hồi crash, file dang dởKhông khả thi cho sổ batch/lớn
Queue BullMQRetry/backoff có sẵnThêm một bề mặt hạ tầng; Kafka đã có trong stackTái dùng Kafka sẵn thay vì thêm ngữ nghĩa Redis-queue
Auto-replay khi consume thất bạiTự chữaRủi ro bão poison-message khi parse thất bại tất địnhRetry thủ công + quét kẹt có giới hạn an toàn hơn

Tham chiếu

  • ledger/src/services/ledger-queue.service.ts (handleEnqueueGeneration)
  • ledger/src/services/ledger-worker.service.ts (handleGeneration)
  • ledger/src/components/kafka.component.ts (consumer autocommit: false)
  • ledger/src/components/recovery.component.ts (quét kẹt)
  • Generation Pipeline

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.