Queue Reference
1. Queue đang có
| Queue | Vai trò | Cấu hình |
|---|---|---|
nemo12-events | Mọi domain event của hệ thống | max_batch_size: 25 · max_retries: 5 · DLQ nemo12-events-dlq |
nemo12-events-dlq | Dead letter — message thất bại quá 5 lần | Chưa có consumer; xử lý thủ công |
Khai báo tại workers/api/wrangler.jsonc. Producer binding: EVENTS. Consumer: chính worker nemo12-api (export default { queue }).
Danh sách 19 loại event và payload từng loại: Event Catalog (sinh tự động từ code).
2. Envelope
{
event_id: string, // UUID, duy nhất cho mỗi lần publish
idempotency_key: string, // ổn định theo NGHIỆP VỤ — cùng nghiệp vụ = cùng key
type: string, // "EvidenceRecorded", "GoalSet", …
occurred_at: string, // ISO
producer: "api",
version: 1,
payload: Record<string, unknown>
}Phân biệt hai khóa: event_id đổi mỗi lần gửi; idempotency_key không đổi cho cùng một sự việc. Consumer phải khử trùng theo idempotency_key, không theo event_id.
Ví dụ: warmup:${sessionId}, exam-attempt:${attemptId}, orca:${learnerId}:${competitionId}:${state}.
3. Publish — không được làm hỏng nghiệp vụ chính
try {
await queue.send(event);
} catch (err) {
console.error("EVENT_PUBLISH_FAILED", { type, idempotencyKey, err });
}Hai luật:
- Publish SAU khi commit. Không bao giờ phát event cho việc chưa chắc đã xảy ra.
- Lỗi publish bị nuốt. Queue chết không được phép làm học sinh không nộp được bài (SDD-006 §2). Đánh đổi: mất event. Chấp nhận được vì hiện không có nghiệp vụ nào phụ thuộc vào event để đúng — xem §4.
Tìm sự cố: lọc log EVENT_PUBLISH_FAILED trong Workers observability (đã bật trong wrangler.jsonc).
4. Consumer — trạng thái thật
async queue(batch: MessageBatch<unknown>, _env: Env): Promise<void> {
for (const message of batch.messages) {
console.log("event", { id: message.id, body: message.body });
message.ack();
}
}Consumer hiện là stub
Consumer chỉ log rồi ack. Chưa có handler nghiệp vụ nào.
Điều này có chủ ý ở giai đoạn hiện tại: mọi thứ cần chạy sau một sự kiện đang được gọi đồng bộ ngay trong request (ví dụ runModelsSafely() sau khi nộp bài). Nhờ vậy hệ thống đúng ngay cả khi queue chết hoàn toàn, và event đóng vai trò nhật ký + đường mở rộng, chưa phải đường thi hành.
Hệ quả cần nhớ: đừng chuyển nghiệp vụ nào sang queue mà không viết consumer trước. Publish thành công không có nghĩa là có ai đó xử lý.
Khi viết consumer thật — bắt buộc
| Yêu cầu | Vì sao |
|---|---|
Idempotent theo idempotency_key | Queue là at-least-once. Cùng message sẽ đến hai lần. |
ack() khi xong, retry() khi lỗi tạm | ack() mù như hiện nay sẽ nuốt luôn lỗi thật |
| Không gọi lại API của chính mình | Vòng lặp event |
| Ghi run log nếu chạy engine | Truy vết được (xem workflows) |
| Chịu được message của version cũ | version trong envelope tồn tại để làm việc này |
DLQ
Quá 5 lần retry → nemo12-events-dlq. Hiện chưa có consumer và chưa có cảnh báo — nghĩa là message chết ở đó im lặng. Trước khi có nghiệp vụ thật chạy qua queue, phải có ít nhất một cảnh báo khi DLQ có message (QG-009).
Trace
- REQ-PLT-03 (event-driven), REQ-NFR-01.
- Thiết kế: SDD-001 §8, SDD-006 §2.
- Kiểm chứng: QG-009.
- Liên quan: Event Catalog · Schedules · Webhooks.