You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

setInterval执行过期OTP记录删除任务时出现无限循环问题

定时任务每三个月删除过期OTP记录却陷入无限循环的问题解决

问题场景

需求为每三个月自动删除所有已过期的OTP记录,实现代码如下,但实际运行时函数频繁执行,日志疯狂输出,陷入无限循环。

代码实现

/sheduled-tasks/OTPRecords.js

const prisma = require("../services/prisma");

const THREE_MONTHS = 1000 * 60 * 60 * 24 * 90;

async function deleteExpiredOTPRecords() {
    console.log("first");
    setInterval(async () => {
      console.log("INTERVAL", THREE_MONTHS);
      const today = new Date();
      const yesterday = new Date(today.setDate(today.getDate() - 1));
      const records = await prisma.otp.deleteMany({
        where: { expires_at: { lte: yesterday } },
      });
      console.log(records);
    }, THREE_MONTHS);
}
module.exports = deleteExpiredOTPRecords;

/index.js

deleteExpiredOTPRecords();

执行输出

INTERVAL 7776000000
INTERVAL 7776000000
{ count: 0 }
{ count: 0 }
INTERVAL 7776000000
{ count: 0 }
...(重复输出)
^C(node:33075) [DEP0164] DeprecationWarning: Implicit coercion to integer for exit code is deprecated.

问题根因

JavaScript的setInterval(及setTimeout)接受的延迟参数是32位有符号整数,最大值为2147483647毫秒(约24.8天)。你的THREE_MONTHS计算值为7776000000毫秒,远超过该最大值,会被自动截断为负数。而setInterval对负数延迟的处理逻辑是立即执行回调,之后以近似0毫秒的间隔重复触发,最终导致函数疯狂执行。

另外原代码中日期处理存在隐性bug:today.setDate()会直接修改today本身,导致today和yesterday指向同一个日期对象。

修复方案

改用嵌套setTimeout实现超长期定时任务,每次任务执行完成后再设置下一次延迟,同时修正日期处理逻辑:

修改/sheduled-tasks/OTPRecords.js代码:

const prisma = require("../services/prisma");

const THREE_MONTHS = 1000 * 60 * 60 * 24 * 90;

async function deleteExpiredOTPRecords() {
    console.log("Initializing OTP cleanup task");
    
    // 封装任务执行逻辑
    async function runCleanup() {
        console.log("Running OTP expired records cleanup");
        const today = new Date();
        // 先复制日期对象再修改,避免污染原变量
        const yesterday = new Date(today);
        yesterday.setDate(today.getDate() - 1);
        
        const deleteResult = await prisma.otp.deleteMany({
            where: { expires_at: { lte: yesterday } },
        });
        console.log("Cleanup completed, deleted records count:", deleteResult.count);
        
        // 任务完成后,设置下一次执行的延迟
        setTimeout(runCleanup, THREE_MONTHS);
    }

    // 首次执行任务
    runCleanup();
}
module.exports = deleteExpiredOTPRecords;

额外建议

对于超长时间的定时任务,若服务重启会导致任务丢失,生产环境更推荐使用专门的任务调度工具(如node-schedule、Agenda)或系统级cron任务来实现,可靠性更高。

内容的提问来源于stack exchange,提问作者Shahed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 19:35:05