Як я можу переконатися, що робота не працює двічі в Bull?


11

Я маю дві функції, scheduleScan() і scan().

scan()дзвінки, scheduleScan() коли немає нічого іншого, крім планування нового сканування , такscheduleScan() можна запланувати а scan(). Але є проблема, деякі роботи виконуються двічі.

Я хочу переконатися, що в будь-який час обробляється лише одна робота. Як я можу цього досягти? Я вірю, що це має щось спільнеdone() (він був у скануванні (), видалено зараз), але я не зміг знайти рішення.

Версія бика: 3.12.1

Важливе запізніле редагування: scan() викликає інші функції, вони можуть або не можуть викликати інші функції, але всі вони функції синхронізації, тому вони викликають функцію лише тоді, коли власні завдання виконані, є лише один шлях вперед. Наприкінці "дерева" я його називаю, остання функція викликає rasporedScan (), але не може бути виконано два одночасні завдання. Кожна робота починається scan(), до речі, і закінчуєтьсяscheduleScan(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)
      })
    }
  })
}

Я не можу знайти scanфункцію, ви можете допомогти?
Мухаммед Зеешан

@MuhammadZeeshan Я додав це, моя помилка.
розпродаж

Відповіді:


6

Я вважаю, що проблема scanполягає в асинхронізації. Тому ваша job.progressфункція дзвонить, scanа потім негайно дзвонитьdone дозволяючи черзі обробляти інше завдання.

Рішенням може бути передача doneзворотного дзвінка як параметр вашому scanтаscheduleScan функціям та виклик його, як тільки ви закінчите роботу (або помилка).

Іншим (кращим) рішенням може бути забезпечення того, що ви завжди повертаєтесь Promiseіз scanта scheduleScan, а потім чекаєте обіцянки вирішити, а потім зателефонувати done. Якщо ви робите це, переконайтеся, що ви ланцюжок усіх своїх обіцянок повертає у вашій scheduleScanфункції.

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;
             })

}

Я відредагував своє запитання, чи можете ви його ще раз перевірити, особливо "Важлива пізня редакція"? Чи все ще ваша відповідь застосовується в цій ситуації? Дякую.
розпродаж

1
Так, воно все ще діє. З вашого редагування, я думаю, ви говорите scheduledScan, що завжди викликається після всіх інших функцій синхронізації в scan. Якщо це так, то так, моя відповідь все ще справедлива. Просто завжди повертає обіцянку , що буде повернуто з scheduleScanв scanфункції
Дживс

Знову моя помилка. Перша функція, update (), знаходиться в скануванні, але update () може викликати іншу функцію, як finalize (), і finalize () може call urneScan (). Будь ласка, майте на увазі, що це відбувається в порядку, тому немає декількох дзвінків, я роблю це, щоб додаток було модульним. - Спасибі
розпродаж

1
Так, та сама відповідь. Якщо updateдзвінки scheduledScanабо будь-яка кількість функцій між ними. Ключовим моментом є те, що потрібно повернути ланцюжок обіцянок з scheduleScanусієї дороги назад до scanфункції. Тож якщо scanдзвінки, updateякі дзвонять finalise..... Який виклик scheduleScanланцюга обіцянок потрібно буде повернути через усі виклики функцій, тобто просто переконайтеся, що ви повертаєте обіцянку від кожної з цих функцій.
джипи

Тож просто для уточнення мого останнього коментаря. Наприклад, якщо всередині сканування ви викликаєте оновлення. Вам потрібно повернути результат оновлення (обіцянку) з функції сканування.
джипи

4

Функція сканування - це асинхронна функція. У своїй queue.process()функції ви повинні дочекатися функції сканування, а потім викликати done()зворотний дзвінок.

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 });
  }
}

Спробуйте це! Я спробував трохи переробити код за допомогою функції async-await.


Я відредагував своє запитання, чи можете ви його ще раз перевірити, особливо "Важлива пізня редакція"? Чи все ще ваша відповідь застосовується в цій ситуації? Дякую.
розпродаж
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.