简体   繁体   中英

Dynamic Crons are executed many times at the same time

I'm creating new CronJobs and scheduling them to run in the future, but when the execution time arrives, the same Job is fired three times. After the execution of the job I am removing it from the registry and even so it does not avoid the tripling of the job.

localhost it's triggered onnly once
published it's triggered thrice

we have three pods behind kube.netes. i guess is something related with that.

 const date = dateFns.addMinutes(new Date(), 10);
 const job = new CronJob({
      cronTime: date,
      start: true,
      onTick: async () => {
      await this.sendEmail(params);
    }
 });
this.schedulerRegistry.addCronJob('job01', job);

Based on your reply in the comment, you are using clusters. So for every instance that is running, a cron is created. If you have three instances in the cluster, you will get three crons. What you need to do is assign a name to your cluster. I'll give you an example of the setup we have.

  • We are running instances based on max cpu cores.

  • We assigned a name to the instances, and gave one of them the name primary . And set it to run on one core.

  • The remaining instances doesn't matter what you name them, but we set the count to -1 . This way we utilize all cores on the machine.

Here's an example of the ecosystem.config.js

module.exports = {
  apps: [
    {
      name: 'nest-primary',
      script: './dist/src/main.js',
      instances: '1',
      exec_mode: 'cluster',
      time: true,
      combine_logs: true,
      max_memory_restart: '3500M',
      max_old_space_size: 3000,
      log_date_format: 'HH:mm YYYY-MM-DD Z',
      log_type: 'json',
      merge_logs: true,
      env_local: {
        NODE_ENV: 'local',
        HOST: 'localhost',
        PORT: 3000,
        DATABASE_URL: 'mysql://user:password@localhost:3306/himam',
        DATABASE_URL_PG: 'postgresql://postgres:password@localhost:5432/himam',
      },
      env_development: {
        NODE_ENV: 'development',
        PORT: 3000,
        HOST: '0.0.0.0',
        DATABASE_URL: 'mysql://user:password@localhost:3306/himam',
        DATABASE_URL_PG: 'postgresql://postgres:password@localhost:5432/himam',
      },
  },
  {
      name: 'nest-replica',
      script: './dist/src/main.js',
      instances: '-1',
      exec_mode: 'cluster',
      time: true,
      combine_logs: true,
      max_memory_restart: '3500M',
      max_old_space_size: 3000,
      log_date_format: 'HH:mm YYYY-MM-DD Z',
      log_type: 'json',
      merge_logs: true,
      env_local: {
        NODE_ENV: 'local',
        HOST: 'localhost',
        PORT: 3000,
        DATABASE_URL: 'mysql://user:password@localhost:3306/himam',
        DATABASE_URL_PG: 'postgresql://postgres:password@localhost:5432/himam',
      },
      env_development: {
        NODE_ENV: 'development',
        PORT: 3000,
        HOST: '0.0.0.0',
        DATABASE_URL: 'mysql://user:password@localhost:3306/himam',
        DATABASE_URL_PG: 'postgresql://postgres:password@localhost:5432/himam',
      },
  },

When I launch the cluster, I pass --env production

pm2 start ecosystem.config.js --env production

The most important part, in your crons, you need to check the name of the instance. You can do this by adding the names you used in the config above to your .env

PM2_PRIMARY_NAME=nest-primary
PM2_REPLICA_NAME=nest-replica
  • Finally, in your code when you want to run the cron, check the name of the process, like this:
 async handleCron() {
    if (process.env.name !== this.configService.get('PM2_PRIMARY_NAME')) {
      return;
    }
    // do your cron logic here.

This ensures that your cron will run only once, because your primary instance is only running on 1 core, and you won't have duplicate triggers. Please do update us.

thanks for the help: I was able to resolve this using cron from the yaml file:

   cronjob:
        use: true
        schedule: '*/5 * * * *'
        env:
            CRON: 1

and call that on bootstrap app like that:

 appBootstrapInstance.bootstrap().then((app) => { return app; }).then(async (app) => { const env = app.get(EnvService); if (env.getAsBoolean('CRON')) { await new Promise((resolve) => setTimeout(resolve, CRON_TIMEOUT)); const task = app.get(MyTaskService); await task.doSomething(); await new Promise((resolve) => setTimeout(resolve, CRON_TIMEOUT)); });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM