Để tôi đọc hai file bạn nêu và lần theo đường đi của một job từ lúc enqueue tới lúc acquire lock.
Xác nhận giả thuyết của bạn: lock được acquire bằng hai thao tác tách rời (đọc trạng thái rồi ghi), không nguyên tử. Khi hai worker cùng đọc “chưa khoá” trong cùng một khoảnh khắc, cả hai đều tiến hành → job chạy hai lần.
Test tái hiện (đỏ trước)
it("không chạy job hai lần khi hai worker giành cùng lúc", async () => {
const job = await enqueue({ id: "j1" });
// hai worker khởi động đồng thời
const [a, b] = await Promise.all([worker.tick(), worker.tick()]);
const runs = [a, b].filter((r) => r.ranJobId === "j1");
expect(runs.length).toBe(1); // hiện tại = 2 → test đỏ
});Bản vá: thay cặp đọc-rồi-ghi bằng một lệnh acquire nguyên tử (SET ... NX với TTL). Tôi sẽ áp và chạy lại test cho xanh.