Vấn đề không phải một bug đơn lẻ, mà là chuỗi lệch pha giữa scheduler, worker concurrency, API Redis runtime, và chiến lược logging. Cách giải quyết hiệu quả là: cô lập triệu chứng theo từng lớp (scheduler → lock → pop batch → parse payload), chứng minh bằng dữ liệu runtime, rồi chốt lại bằng implementation tương thích client hiện tại + issue note để team không lặp lại.
1) Bối Cảnh & Triệu Chứng
Triệu chứng đầu tiên: worker log Processing add like jobs... liên tục, tần suất không đều,
nhiều timestamp trùng nhau theo cặp. Cảm giác ban đầu giống "job chạy đúng nhưng không ăn dữ liệu".
Sau khi thêm lock log, tiếp tục thấy nhiều lần trigger và xuất hiện lỗi rỗng error: {},
khiến việc tìm nguyên nhân thật bị mù thông tin.
- Queue repeat có dấu hiệu chồng scheduler cũ
- Job có lúc chạy dày hơn 5s (không đều)
- Log lỗi thiếu
message/stacknên khó truy nguyên - Dùng API Redis không tương thích runtime hiện tại
- Scheduler được cleanup trước khi tạo lại
- Có lock token theo tiến trình để tránh chạy chồng
- Luồng pop batch dùng API tương thích chắc chắn
- Tài liệu hóa đủ để tái hiện và mở issue chuẩn
2) Timeline Debug (Theo Thứ Tự Thật)
SET key value NX EX + token ownership khi release.TypeError: redisService.lMPop is not a function.redis@5.12.1 không dùng signature như giả định ban đầu.rPop thay vì phụ thuộc method không ổn định.3) Kỹ Thuật Tra Cứu: Vì Sao Kết Luận Như Vậy
Điểm quan trọng là phân biệt rõ 3 lớp: docs bên ngoài, type definitions trong local, và behavior runtime thực tế. Kết luận chỉ chốt khi cả 3 cùng khớp.
Dữ liệu runtime (log thật) evidence
Bằng chứng quyết định: lỗi runtime báo trực tiếp lMPop is not a function. Nghĩa là object client hiện tại không expose method đó theo cách đang gọi.
[2026-05-14 13:31:00 +0700] ERROR Like sync job failed TypeError: import_redis.redisService.lMPop is not a function at LikeWorker.popElements (.../workers/like.worker.ts:129:41)
Tra cứu package cài thật trong project evidence
Đã kiểm tra dependency thực tế: redis@5.12.1 và @redis/client@5.12.1. Đây là nguồn sự thật của API đang chạy.
npm ls redis @redis/client --depth=1 threads-api-v2 +-- redis@5.12.1 `-- @redis/client@5.12.1
Tra cứu d.ts để đối chiếu signature evidence
Trong declaration cục bộ, có module LMPOP, RPOP, RPOP_COUNT. Nhưng call-site thực tế của client không match assumption trước đó. Kết luận: phải gọi theo API đã kiểm chứng hoặc dùng phương án tương thích tối đa.
node_modules/@redis/client/dist/lib/commands/RPOP.d.ts parseCommand(parser, key) // 1 arg node_modules/@redis/client/dist/lib/commands/RPOP_COUNT.d.ts parseCommand(parser, key, count) node_modules/@redis/client/dist/lib/commands/LMPOP.d.ts exists in command module, but runtime method mapping may differ
4) Thay Đổi Kỹ Thuật Đã Áp Dụng
Phần này gom cả thay đổi cuối cùng và lý do kiến trúc đằng sau từng thay đổi.
workers/like.worker.ts mod +84 −49
Distributed lock + pop strategy tương thích. Bổ sung lock chống chạy chồng, nhóm theo post để giảm write, parse an toàn từng item, và pop tuần tự bằng rPop để không phụ thuộc API không ổn định trong runtime.
const lockToken = `${process.pid}:...`; const isLocked = await acquireSyncLock(lockToken); if (!isLocked) return; for (let i = 0; i < count; i++) { const item = await redisService.rPop(key); if (!item) break; result.push(item); }
src/modules/job/like-job/producer/like.producer.ts mod +43 −21
Scheduler hygiene. Trước khi add repeat job mới, cleanup các repeatable meta-job cũ cùng name để tránh tích lũy lịch chạy dư thừa qua nhiều lần restart/deploy.
const jobs = await likeQueue.getRepeatableJobs(); const duplicates = jobs.filter(j => j.name === INIT_SYNC_JOB); await Promise.all(duplicates.map(j => likeQueue.removeRepeatableByKey(j.key))); await likeQueue.add(INIT_SYNC_JOB, {}, { repeat: { every: 5000 }, attempts: 1 });
src/server.ts mod +45 −37
Boot sequence an toàn hơn. Dừng hành vi xóa queue khi start app; chuyển sang init scheduler có bắt lỗi riêng để không làm startup fail âm thầm.
src/modules/post/repository/like.repository.ts mod +22 −16
Sửa đúng nghiệp vụ unlike batch. Đổi điều kiện xóa sang pair-wise OR theo từng userId + postId, tránh xóa chéo ngoài ý muốn.
src/modules/post/repository/post.repository.ts mod +30 −35
Điểm chốt \"quá chuẩn\" của commit mới nhất. Hai query tăng/giảm like count đã được đơn giản hóa: bỏ JOIN likes, cập nhật trực tiếp trên bảng posts theo public_id. Cách này giảm coupling vào trạng thái bảng likes tại thời điểm đồng bộ và ổn định hơn khi xử lý batch.
UPDATE posts p JOIN likes l ON l.post_id = p.id SET p.likes_count = GREATEST(p.likes_count ± ${count}, 0) UPDATE posts SET likes_count = GREATEST(likes_count ± ${count}, 0) WHERE public_id = ${publicId}
5) Luồng Module Route (Flowchart)
Section này mô tả luồng đầy đủ từ API route đến worker và repository write path, theo phong cách
flowchart tương tác giống tài liệu 13-flowchart-diagram.
6) Chiến Lược Logging Đã Dùng
Phần này quan trọng vì chính logging strategy quyết định tốc độ tìm root cause.
error: {}, ưu tiên sửa cấu trúc log trước7) Note Công Dụng Tạo Issue (Copy-Paste)
Issue kiểu này rất hữu dụng để team sau không debug lại từ đầu. Nên dùng issue như một hồ sơ kỹ thuật: mô tả triệu chứng, bằng chứng, giả thuyết, thử nghiệm, và quyết định cuối cùng.
## [Like Queue] Repeating sync job runs irregularly + Redis command mismatch ### 1) Symptoms - Worker logs trigger irregularly (not strictly every 5s) - Duplicate-like behavior suspected under repeat scheduling - Runtime error observed: `redisService.lMPop is not a function` ### 2) Impact - Like sync flow unstable - Hard to trust batch processing correctness without root-cause fix ### 3) Root Causes - Scheduler duplication risk from repeated bootstrap - Runtime API mismatch between assumed Redis command method and installed client - Limited observability in early logging phase ### 4) Evidence - Runtime stacktrace from worker - Local package version: redis@5.12.1 - Local d.ts verification under node_modules/@redis/client/dist/lib/commands ### 5) Fix Implemented - Cleanup repeatable jobs before re-initialization - Add lock token with NX/EX strategy - Switch pop batch to compatible rPop loop - Correct unlike delete condition to pair-wise OR ### 6) Verification - Worker acquires lock as expected - No `lMPop` TypeError after fix - Batch logs show parsed item counts and grouped writes ### 7) Follow-up - Add integration test for repeat scheduler duplication - Add runbook note in docs/job-worker-flow.html
8) Test Plan / Verification Matrix
9) Rollout & Vận Hành
Với bài toán worker queue, rollout theo thứ tự tiến trình quan trọng hơn rollout theo endpoint API.