Come posso assicurarmi che un lavoro non venga eseguito due volte in Bull?


11

Ho due funzioni scheduleScan()e scan().

scan()chiama scheduleScan() quando non c'è nient'altro da fare se non programmare una nuova scansione , quindi scheduleScan()può pianificare un scan(). Ma c'è un problema, alcuni lavori vengono eseguiti due volte.

Voglio assicurarmi che venga elaborato un solo lavoro alla volta. Come posso raggiungerlo? Credo che abbia qualcosa a che fare con done()(era in scan (), rimosso ora) ma non sono riuscito a trovare una soluzione.

Versione Bull: 3.12.1

Importante modifica tardiva: scan() chiama un'altra funzione e possono o meno chiamare altre funzioni, ma sono tutte funzioni di sincronizzazione, quindi chiamano una funzione solo quando i loro lavori sono stati completati, c'è solo un modo per andare avanti. Alla fine dell '"albero", lo chiamo, l'ultima funzione chiama scheduleScan (), ma non ci possono essere due lavori simultanei in esecuzione. Ad ogni scan()modo, ogni singolo lavoro inizia e finisce conscheduleScan(stock, period, milliseconds, 'called by file.js')

export function update(job) {
  // does some calculations, then it may call scheduleScan() or
  // it may call another function, and that could be the one calling
  // scheduleScan() function.
  // For instance, a function like finalize()
}

export function scan(job) {
  update(job)
}


import moment from 'moment'
import stringHash from 'string-hash'
const opts = { redis: { port: 6379, host: '127.0.0.1', password: mypassword' } }
let queue = new Queue('scan', opts)

queue.process(1, (job) => {
  job.progress(100).then(() => {
    scan(job)
  })
})

export function scheduleScan (stock, period, milliseconds, triggeredBy) {
  let uniqueId = stringHash(stock + ':' + period)

  queue.getJob(uniqueId).then(job => {
    if (!job) {
      if (milliseconds) {
        queue.add({ stock, period, triggeredBy }, { delay: milliseconds, jobId: uniqueId }).then(() => {
          // console.log('Added with ms: ' + stock + ' ' + period)
        }).catch(err => {
          if (err) {
            console.log('Can not add because it exists ' + new Date())
          }
        })
      } else {
        queue.add({ stock, period, triggeredBy }, { jobId: uniqueId }).then(() => {
          // console.log('Added without ms: ' + stock + ' ' + period)
        }).catch(err => {
          if (err) {
            console.log('Can not add because it exists ' + new Date())
          }
        })
      }
    } else {
      job.getState().then(state => {
        if (state === 'completed') {
          job.remove().then(() => {
            if (milliseconds) {
              queue.add({ stock, period, triggeredBy }, { delay: milliseconds, jobId: uniqueId }).then(() => {
                // console.log('Added with ms: ' + stock + ' ' + period)
              }).catch(err => {
                if (err) {
                  console.log('Can not add because it exists ' + new Date())
                }
              })
            } else {
              queue.add({ stock, period, triggeredBy }, { jobId: uniqueId }).then(() => {
                // console.log('Added without ms: ' + stock + ' ' + period)
              }).catch(err => {
                if (err) {
                  console.log('Can not add because it exists ' + new Date())
                }
              })
            }
          }).catch(err => {
            if (err) {
              // console.log(err)
            }
          })
        }
      }).catch(err => {
        // console.log(err)
      })
    }
  })
}

Non riesco a trovare la scanfunzione, puoi aiutarmi?
Muhammad Zeeshan,

@MuhammadZeeshan L'ho aggiunto, errore mio.
salep

Risposte:


6

Il problema, credo, è che la tua scanfunzione è asincrona. Quindi la tua job.progressfunzione chiama scane quindi chiama immediatamente donepermettendo alla coda di elaborare un altro lavoro.

Una soluzione potrebbe essere quella di passare il donecallback come parametro al proprio scane alle scheduleScanfunzioni e invocarlo, una volta completato il lavoro (o in caso di errore).

Un'altra (migliore) soluzione potrebbe essere quella di assicurarti di restituire sempre un Promiseda scane scheduleScan, quindi attendere la promessa di risolvere e quindi chiamare done. Se lo fai, assicurati di incatenare tutti i tuoi rendimenti promessi nella tua scheduleScanfunzione.

queue.process(1, (job, done) => {
  job.progress(100).then(() => {
    scan(job)
        .then(done)
        .catch(done)
  })
})

export function scan() {
   // business logic
   return scheduleScan()
}

// Chain all of your promise returns. Otherwise
// the scan function will return sooner and allow done to be called
// prior to the scheduleScan function finishing it's execution
export function scheduleScan() {
    return queue.getJob(..).then(() => {
        ....
        return queue.add()...
        ....
        return queue.add(...)
            .catch(e => {
                 console.log(e);
                 // propogate errors!
                 throw e;
             })

}

Ho modificato la mia domanda, puoi per favore controllarla di nuovo, in particolare la parte "Importante modifica in ritardo"? La tua risposta si applica ancora in questa situazione? Grazie.
salep

1
Sì, è ancora valido. Dalla tua modifica, penso che stai dicendo che scheduledScanviene sempre chiamato dopo tutte le altre funzioni di sincronizzazione in scan. In questo caso, sì, la mia risposta è ancora valida. Basta tornare sempre la promessa che verrà restituito dai scheduleScannelle scanfunzioni
Jeeves

Ancora una volta, il mio errore. La prima funzione, update (), è in scan, ma update () può chiamare un'altra funzione come finalize () e finalize () può chiamare scheduleScan (). Tieni presente che questi avvengono in un ordine, quindi non ci sono più chiamate, lo sto facendo per mantenere la mia app modulare. - Grazie
salep

1
Sì, stessa risposta. Se updatechiamate scheduledScano qualsiasi numero di funzioni tra di loro. Il punto chiave è che è necessario riportare la catena della promessa da scheduleScancapo alla scanfunzione. Quindi se scanchiamate updateche chiamano finalise..... Quali chiamate scheduleScanla catena di promesse dovrà essere restituita attraverso tutte le invocazioni di funzioni, vale a dire assicuratevi di restituire la promessa da ciascuna di queste funzioni.
jeeves

Quindi, solo per chiarire il mio ultimo commento. Ad esempio, se all'interno di scan si chiama update. È necessario restituire il risultato dell'aggiornamento (una promessa) dalla funzione di scansione.
jeeves

4

La funzione di scansione è una funzione asincrona. Nella tua queue.process()funzione devi attendere la funzione di scansione e quindi chiamare la done()richiamata.

export async function scan(job) {
  // it does some calculations, then it creates a new schedule.
  return scheduleScan(stock, period, milliseconds, "scan.js");
}

queue.process(1, (job, done) => {
  job.progress(100).then(async() => {
    await scan(job);
    done();
  });
});

export async function scheduleScan(stock, period, milliseconds, triggeredBy) {
    let uniqueId = stringHash(stock + ":" + period);
    try {
      const existingJob = await queue.getJob(uniqueId);
      if (!existingJob) {
        const job = await addJob({
          queue,
          stock,
          period,
          uniqueId,
          milliseconds,
          triggeredBy
        });
        return job;
      } else {
        const jobState = await existingJob.getState();
        if (jobState === "completed") {
          await existingJob.remove();
          const newJob = await addJob({
            queue,
            stock,
            period,
            uniqueId,
            milliseconds,
            triggeredBy
          });
          return newJob;
        }
      }
    } catch (err) {
      throw new Error(err);
    }
}

export function addJob({ queue, stock, period, milliseconds, triggeredBy }) {
  if (milliseconds) {
    return queue.add(
      { stock, period, triggeredBy },
      { delay: milliseconds, jobId: uniqueId }
    );
  } else {
    return queue.add({ stock, period, triggeredBy }, { jobId: uniqueId });
  }
}

Prova questo! Ho provato a riformattare un po 'il codice usando async-waitit.


Ho modificato la mia domanda, puoi per favore controllarla di nuovo, in particolare la parte "Importante modifica in ritardo"? La tua risposta si applica ancora in questa situazione? Grazie.
salep
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.