2012-07-10 81 views
10

我在Go下为MongoDB使用mgo驱动程序。MongoDB in Go(golang)with mgo:如何更新记录,找出更新是否成功,并在单个原子操作中获取数据?

我的应用程序要求一个任务(只需从一个名为“jobs”的集合中选择一个Mongo记录),然后将自己注册为一个asignee来完成该任务(对同一个“job”记录的更新,设置自己作为受让人)。

该程序将在多台机器上运行,所有机器都与同一个Mongo交谈。当我的程序列出可用任务并选择一个时,其他实例可能已经获得了该分配,并且当前分配失败。

如何确保在更新时我读取并更新的记录确实或没有特定的值(在本例中为受让人)?

我想得到一个任务,无论是哪一个,所以我认为我应该先选择一个待处理任务并尝试分配它,保持它只是在更新成功的情况下。

所以,我的查询应该是这样的:

“从上收集‘工作’的所有记录,更新只是一个有asignee = null时,设置我的ID作为受让人然后,给我。那记录所以我可以运行这项工作。“

我怎么能用mgo驱动程序表达这个问题呢?

回答

2

我希望你看到了对你选择的答案的评论,但是这种方法是不正确的。做一个选择然后更新将导致往返和两台机器,并获取相同的工作,然后其中一个可以更新assignee。您需要使用findAndModify方法代替:http://www.mongodb.org/display/DOCS/findAndModify+Command

+0

其实,我需要的解决方案是提供的一个tux21b。我需要撤消所有“选项”,然后选择一个,然后尝试将其分配给我自己。如果不成功,我会尝试与另一个。 – 2012-09-10 18:20:15

+0

为什么你需要选择所有的选项?你有没有说你只需要选择没有采取(又名assignee == null)? – 2012-09-11 16:12:18

+0

你说得对。我的需求结果与我在写这个问题时想的不同,但你的回答更适合这个问题。 – 2012-09-11 21:12:03

2

MongoDB的人描述的官方文档中类似的情景:http://www.mongodb.org/display/DOCS/Atomic+Operations

基本上,所有你需要做的,是assignee=null获取任何工作。假设你在_id=42之后找到了工作。然后,您可以继续并在本地修改文档,方法是设置assignee="worker1.example.com"并使用选择器{_id=42, assignee=null}和您的更新文档致电Collection.Update()。如果数据库仍然能够找到与此选择器匹配的文档,它将以原子方式替换文档。否则,你会得到一个ErrNotFound,表示另一个线程已经声明了这个任务。如果是这种情况,请再试一次。

+4

执行选择之后的更新不是原子;它执行两次往返! findAndModify命令将以原子方式执行此操作。 此命令的Mongo文档位于:http://www.mongodb.org/display/DOCS/findAndModify+Command 在Go中执行此操作的Mgo文档位于:http://go.pkgdoc.org/labix .ORG/V2 /氧化镁#查询。应用 – jorelli 2012-07-20 13:30:48

+1

对于此特定算法,select + update不需要是原子的。只要更新本身是原子的(这基本上是一个CompareAndSwap/CompareExchange操作),一切都很好。 – tux21b 2012-07-21 05:56:34

+1

如果两台机器在第一个查询中选择相同的作业,则其中一个机器将在更新时失败。当然,最终结果可能总是数据库中的数据永远不会变得不一致,但使用两个单独操作的失败情况会导致大量不必要的来回。这是... findAndModify命令存在的全部原因。 – jorelli 2012-07-21 18:10:20

41

这是一个古老的问题,但以防万一有人仍在家中观看,这很好地支持通过Query.Apply方法。它确实运行findAndModify命令,如另一个答案中所示,但它很方便隐藏在Go善良背后。

文档中的例子几乎完全的问题在这里一致:

change := mgo.Change{ 
     Update: bson.M{"$inc": bson.M{"n": 1}}, 
     ReturnNew: true, 
} 
info, err = col.Find(M{"_id": id}).Apply(change, &doc) 
fmt.Println(doc.N) 
相关问题