MongoDB多步骤事务问题

时间:2011-07-15 18:20:39

标签: email mongodb transactions

如果我们需要更新CUSTOMER文档并在同一笔交易中发送电子邮件,那么确保以原子方式完成此操作的最佳方法是什么?我们正在建立一个ecomm网站,我们需要此功能,因为当客户购买商品时,我们必须更新订单历史记录并向他们发送电子邮件确认。在使用RDBMS数据库的Java中,我们可以通过简单地更新数据库并发送包含电子邮件内容和详细信息的JMS消息来轻松完成此操作; JDBC和JMS都支持分布式事务,因此如果出现问题可以回滚,但MongoDB则不然。 Mongo中是否有消息传递功能?

我们考虑在Customer的orderHistory嵌入文档中使用标记“emailSentFlag”。下订单时,该标志设置为false。然后,我们将使用外部作业扫描所有订单历史记录,其中emailSentFlag =“false”并在此时发送电子邮件,但这使我们回到相同的情况,因为我们必须在发送后将标志设置回“true”电子邮件,这不是原子的。

> customer {
>     name:
>     email:
>     orderHistory{
>        orderId:
>        status:
>        emailSentFlag:

4 个答案:

答案 0 :(得分:2)

其他人已经说过Mongo没有交易。

我建议您构建邮件队列而不是设置标志,然后搜索整个集合。我宁愿在邮件队列中放入一个条目,然后使用外部作业处理该队列。

至少我会先尝试这样做。任何人,任何评论好还是坏?

答案 1 :(得分:1)

您可以在DOCUMENT级别执行原子操作: http://www.mongodb.org/display/DOCS/Atomic+Operations

我有一个用于排队电子邮件的集合,并在电子邮件文档中放置一个字段,以便在发送电子邮件时更新记录。然后让您的发送作业在发送完成后更新父记录。

您甚至可以使用电子邮件文档中的DB Ref作为客户文档。

答案 2 :(得分:1)

值得思考这些行为是否真的需要以原子方式发生:

  • 添加订单记录订单项
  • 发送确认电子邮件

发送电子邮件的回滚条件是什么?无效的邮件地址?无法连接到邮件服务器?已发送电子邮件但未收到? 您是否要取消客户的交易,因为邮件服务器已关闭?

我说可以使用customer.orderHistory.emailSentFlag创建新的订单行,然后有一个预定作业来处理emailSentFlag = 0订单以发送确认电子邮件,然后在电子邮件发送后设置标记已成功发送。

如果您获得退回,或由于某种原因交付失败,您可以随时重置旗帜并重试。或者更好的是,将电子邮件作业发送到邮件排队系统,为您处理所有这些内容。

答案 3 :(得分:0)

这是一个架构较少的数据库,所以利用它! :-)您可以使用duck typing,但是将字段添加到跨类型识别的任何文档类型。确保使用文档中未使用的名称。

在任何包含任务的文档(如发送电子邮件)中添加tasks: []数组。当您更新文档并且需要与其关联的“原子”东西时,将任务推送到此队列。例如,对于电子邮件:{ email: "cust@client.com", at: 0 }。后台进程轮询这些任务,将它们设置为将来过期,执行它们并删除它们。如果某些地方失败,任务将过期并重新运行。所以你可能发送两封邮件,但邮件或多或少是不可避免的,即使是交易也是如此。

你可以在mongodb shell中运行它:

#db.orders.remove()
db.orders.save( { name: "abc", tasks : [  { email: 'a@dom.com', at: 0}, { email: 'b@dom.com', at: 0} ] })
db.orders.save( { name: "def", tasks : [  { email: 'c@dom.com', at: 0}] })

now  = 10; future = 100; me = "some unique identifier for this scanning process"

while ( true ) {
  task = db.orders.findAndModify({
    query:   { "tasks.at": { $lt: now }},
    update : { $set : { "tasks.$.at": future, "tasks.$.who": me } }, 
    fields:  { "tasks":1},
    new:     true } )

  if ( !task)
    break;

  for ( var i in task.tasks ) {
    if ( task.tasks[i].who === me )
      print( "Send email " + task.tasks[i].email )  
  }

  db.orders.update( { _id: task._id }, { $pull: { tasks: { who: me } } } )
}

查看http://www.mongodb.org/display/DOCS/findAndModify+Command的类似示例。