Bulls 队列性能和可扩展性:Queue.add()、Queue.getJob(jobId)、Job.remove()

Bulls Queue Performance and Scalability: Queue.add(), Queue.getJob(jobId), Job.remove()

我的用例是创建动态延迟 作业。 (我正在使用 Bulls Queue 可用于创建延迟作业。)

基于一些事件延迟间隔添加更多延迟(进一步延迟作业)。

因为我找不到任何 function 来更新作业的延迟间隔,所以我想出了以下步骤:

onEvent(jobId):
  // queue is of Type Bull.Queue
  // job is of type bull.Job
  job = queue.getJob(jobId)
  data = job.data
  delay = job.toJSON().delay
  job.remove()
  queue.add("jobName", {value: 1}, {jobId: jobId, delayed: delay + someValue})

这几乎解决了我的问题。

但我担心这些操作会发生的 SCALE

我预计在不久的将来每分钟将近 50K 个事件,甚至更多。

我的 Queue 大小预计会根据唯一 JobId.

增长

我的期望超过:

此外,在60-70天后,作业的延迟间隔将达到,旧的作业将被一个一个地删除。

我可以 运行 多个处理器来处理这些延迟的作业,这不是问题。

我的队列大小将在 60-70 天后稳定下来,我的队列或多或少将有大约 1000 万个作业。

我可以根据需要垂直缩放我的 REDIS

但我想了解以下查询的时间复杂度:

 queue.getJob(jobId)  // Get Job By Id
 job.remove() // remove job from queue
 queue.add(name, data, opts) // add a delayed job to this queue

如果这些操作中的任何一个是 O(N) 或者 QUEUE 可以保留一些最大数量的 Jobs不到1000万.

然后我可能不得不放弃这个设计并想出一些完全不同的东西。

需要有经验的人的建议,他们可以指导我如何解决这个问题。

感谢任何形式的帮助。

参考源码:

queue.getJob(jobId)

这应该是 O(1),因为它主要使用基于 hash 的 hmget 解决方案。您只要求一份工作,根据官方 redis 文档,时间复杂度为 O(N),其中 N 是请求的键数,顺序为 O(1),因为我预计公牛存储的很少哈希键的字段数。

job.remove()

考虑到您的大量作业将被延迟,其中一小部分将移至等待或活动队列。这应该是 O(logN) 在摊销水平上,因为它主要使用 zrem 进行这些操作。

queue.add(name, data, opts)

对于延迟队列中的作业添加,bull 使用 zadd 所以这又是 O(logN)。