如何正确处理原子批处理?

时间:2012-12-26 11:24:18

标签: ruby redis batch-processing

在我的应用程序中,有一个批处理机制。它(理论上)在redis列表中累积项目,然后立即处理它们并清理(如果需要)。这是代码:

def with_private_key redis, name
  return unless redis.exists name

  # atomically rename to a random name, so that the batch isn't appended to.
  tmpname = "temp:#{SecureRandom.hex}"
  redis.rename name, tmpname

  # get batch elements
  batch = redis.lrange(tmpname, 0, -1).map{|b| JSON.parse(b) }

  begin
    # actually do something with the batch
    yield batch
  rescue => ex
    # revert and give up
    redis.rename tmpname, name
    raise
  else
    # if there was no exception - drop the temp key, it's not needed anymore
    redis.del tmpname
  end
end

这是这个实现的一个小问题:有时临时密钥不会被删除并保留在数据库中。当我撰写这个问题时,我意识到反向重命名可能会失败,因为已经使用新批次创建了新密钥。

那么,我该如何改进这个算法?

  • 修改批处理不起作用。 LRANGE + LTRIM对不是原子的。
  • 定期检查临时密钥并尝试重新处理它们(命名公式必须更改为包含app_id,但这可能不重要。)
  • 还有别的吗?也许一些Lua技巧(还没有与Lua合作)?

编辑:

我看到我关于重命名的理论是无效的。 RENAME command

  

将键重命名为newkey。当源名称和目标名称相同或者密钥不存在时,它会返回错误。如果newkey已经存在,则会被覆盖。

1 个答案:

答案 0 :(得分:0)

我会尝试

batch, _ = redis.multi do |r|
  r.lrange name, 0, -1
  r.ltrim name, 0, -1
end

如果客户端在获取批次并修剪列表后崩溃,则可能会丢失批次。