我在Go下使用MongoDB的mgo驱动程序。
我的应用程序要求执行一项任务(仅在名为“jobs”的集合中选择Mongo中的记录),然后将自己注册为asignee以完成该任务(对同一“作业”记录的更新,将其自身设置为受让人)。
该程序将在几台机器上运行,所有机器都与同一个Mongo交谈。当我的程序列出可用任务然后选择一个时,其他实例可能已经获得该分配,并且当前分配将失败。
我怎样才能确保我读取并更新的记录在更新时是否具有特定价值(在本例中为受让人)?
我试图获得一项任务,无论是哪一项,所以我认为我应该首先选择一项待处理任务并尝试分配它,只是在更新成功的情况下保留它。
所以,我的查询应该是这样的:
“从收集'工作'的所有记录中,更新只有一个 asignee = null,将我的ID设置为受让人。然后,给我那条记录所以我可以胜任这份工作。“
我怎么能用Go的mgo驱动程序来表达呢?
答案 0 :(得分:41)
这是一个老问题,但万一有人还在家里观看,这可以通过Query.Apply方法得到很好的支持。它确实运行findAndModify
命令,如另一个答案所示,但它很方便地隐藏在Go goodness背后。
文档中的示例几乎完全符合这里的问题:
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)
答案 1 :(得分: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,表明另一个线程已经声明了该任务。如果是这种情况,请再试一次。
答案 2 :(得分:2)
我希望您看到对所选答案的评论,但这种方法不正确。执行选择然后更新将导致往返和两台计算机并且在其中一台计算机可以更新assignee
之前获取相同的作业。您需要使用findAndModify
方法:http://www.mongodb.org/display/DOCS/findAndModify+Command