我正在构建针对Amazon's SQS Standard Queue的模型,该模型可以不按顺序发送更新。
我的目标是正确订购它们。
我很想将队列中的所有数据复制到我的数据库中。
表示例 - 假设我提取了一些消息并处理它们
id | published_at | run_at | payload
1 | 1:11pm | nil | ...
2 | 1:12pm | nil | ...
3 | 1:13pm | nil | ...
4 | 1:14pm | nil | ...
5 | 1:15pm | nil | ...
然后我再拿一些,我们可以看到一些奇怪的消息现在已经过时了。
id | published_at | run_at | payload
1 | 1:11pm | 1:15 | ...
2 | 1:12pm | 1:15 | ...
3 | 1:13pm | 1:15 | ...
4 | 1:14pm | 1:15 | ...
5 | 1:15pm | 1:15 | ...
6 | 1:13pm | nil | ...
7 | 1:14pm | nil | ...
8 | 1:16pm | nil | ...
如果我要通过published_at订购,您可以看到需要从ID=6
开始重新处理队列,以确保按顺序处理消息。
id | published_at | run_at | payload
1 | 1:11pm | 1:15 | ...
2 | 1:12pm | 1:15 | ...
3 | 1:13pm | 1:15 | ...
6 | 1:13pm | nil | ...
4 | 1:14pm | 1:15 | ...
7 | 1:14pm | nil | ...
5 | 1:15pm | 1:15 | ...
8 | 1:16pm | nil | ...
处理数据准确有价值,处理两次的问题很少,因此重新运行不是问题。
我最好奇怪如何最好地找到尚未运行的最旧项目,并从那一刻起开始运行。
目前正在做:
# fetch oldest publish_time that has not been ran
first_publish_time = AnyOfferChange.where(run_at: nil).minimum(:publish_time)
if first_publish_time
# start there, and process in ascending order
AnyOfferChange.order("publish_time DESC").where("publish_time >= ?",first_publish_time).reverse.each(&:process!)
end
感觉非常脆弱,我想取得这个位置并将其作为限制。
limit = AnyOfferChange.where(run_at: nil).order("publish_time ASC").pluck("POSITION SOMETHIN(SOMETHING)").first
if limit > 0
# start there, and process in ascending order
AnyOfferChange.order("publish_time DESC").limit(limit).reverse.each(&:process!)
end
答案 0 :(得分:0)
以下SQL查询将为您提供最早的 publish_time :
AnyOfferChange.where(run_at: nil).minimum(:publish_time)
或者,如果你想要一条记录:
AnyOfferChange.where(run_at: nil).order(publish_time: :asc).first
这会将SQL查询限制为尚未运行的最旧行。
获取所有未从旧到新的记录:
result = AnyOfferChanges.where(run_at: nil).order(publish_time: :asc)
# or
result = AnyOfferChanges.where(run_at: nil).order(:publish_time) # Defaults to :asc
result.each(&:process!) # Process result. See note below for batch info.
获取所有未使用最早的publish_time(未运行)运行的记录:
# See note below to prevent unwanted SQL execution for the statements
# below when executing in the terminal.
# Create shorthand.
any_offer_changes = AnyOfferChange.arel_table
# Build query parts.
not_ran = AnyOfferChange.where(run_at: nil)
oldest_publish_time = not_ran.select(any_offer_changes[:publish_time].minimum)
# All records that not ran with with the oldest publish time.
result = not_ran.where(publish_time: oldest_publish_time)
result.each(&:process!) # Process result. See note below for batch info.
这将导致使用子查询在一个SQL查询中获取具有最低发布时间的所有记录。
我使用除AnyOfferChange.where(run_at: nil).minimum(:publish_time)
以外的其他方式获取最后一部分的最小值的原因。这个查询是否会破坏链并创建多个SQL查询而不是一个。鉴于AnyOfferChange.where(run_at: nil).select(any_offer_changes[:run_at].minimum)
在 where 语句中使用时,链将保持完整。
备注:强>
不需要的SQL执行
当逐个运行时,这将导致多个查询,因为 #inspect (用于显示结果)将触发SQL执行。在终端中使用;nil
跟随每个语句,以防止在构建 #where 链时执行。在脚本中执行时不需要这样做。
使用"批次"
对于大量记录,您可能必须限制结果值。 Rails支持批处理,但他们不尊重给定的订单。为了保持订单,您可以创建自己的批次,尽管可能效率较低。这可以这样做:
result = AnyOfferChange.where(run_at: nil).order(:publish_time).limit(100)
result.each(&:process!) while result.reload.any?
假设您在 #process!中设置 run_at 属性,否则上述操作将导致无限循环。