英文:
MongoDB in Go (golang) with mgo: How do I update a record, find out if update was successful and get the data in a single atomic operation?
问题
我正在使用Go语言下的mgo驱动程序来操作MongoDB。
我的应用程序会请求一个任务(仅从名为"jobs"的集合中选择一条记录),然后将自己注册为该任务的执行者(更新相同的"job"记录,将自己设置为执行者)。
该程序将在多台机器上运行,所有机器都连接到同一个MongoDB。当我的程序列出可用任务并选择一个任务时,其他实例可能已经获取了该任务,并且当前的任务分配将失败。
我如何确保在更新记录时,我读取的记录是否具有特定的值(在这种情况下,是一个执行者)?
我想要获取一个任务分配,无论是哪个任务,所以我认为我应该首先选择一个待处理的任务并尝试分配它,只有在更新成功的情况下才保留它。
因此,我的查询应该是这样的:
"从集合'jobs'的所有记录中,更新只有一个执行者为null的记录,将我的ID设置为执行者。然后,给我那条记录,这样我就可以运行该任务。"
如何使用mgo驱动程序来表达这个查询?
英文:
I am using mgo driver for MongoDB under Go.
My application asks for a task (with just a record select in Mongo from a collection called "jobs") and then registers itself as an assignee to complete that task (an update to that same "job" record, setting itself as assignee).
The program will be running on several machines, all talking to the same Mongo. When my program lists the available tasks and then picks one, other instances might have already obtained that assignment, and the current assignment would have failed.
How can I get sure that the record I read and then update does or does not have a certain value (in this case, an assignee) at the time of being updated?
I am trying to get one assignment, no matter which one, so I think I should first select a pending task and try to assign it, keeping it just in the case the updating was successful.
So, my query should be something like:
"From all records on collection 'jobs', update just one that has assignee=null, setting my ID as the assignee. Then, give me that record so I could run the job."
How could I express that with mgo driver for Go?
答案1
得分: 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)
英文:
This is an old question, but just in case someone is still watching at home, this is nicely supported via the Query.Apply method. It does run the findAndModify
command as indicated in another answer, but it's conveniently hidden behind Go goodness.
The example in the documentation matches pretty much exactly the question here:
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)
答案2
得分: 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错误,表示另一个线程已经声明了该任务。如果是这种情况,请再试一次。
英文:
The MongoDB guys describe a similar scenario in the official documentation: http://www.mongodb.org/display/DOCS/Atomic+Operations
Basically, all you have to do, is to fetch any job with assignee=null
. Let's suppose you get the job with the _id=42
back. You can then go ahead and modify the document locally, by setting assignee="worker1.example.com"
and call Collection.Update() with the selector {_id=42, assignee=null}
and your updated document. If the database is still able to find a document that matches this selector, it will replace the document atomically. Otherwise you will get a ErrNotFound, indicating that another thread has already claimed the task. If that's the case, try again.
答案3
得分: 2
我希望你看到了对你选择的答案的评论,但是那种方法是不正确的。先进行选择然后再更新会导致往返和两台机器,并且在其中一台机器更新assignee
之前,两台机器都会获取相同的工作。你需要使用findAndModify
方法代替:http://www.mongodb.org/display/DOCS/findAndModify+Command
英文:
I hope you saw the comments on the answer you selected, but that approach is incorrect. Doing a select and then update will result in a round trip and two machines and be fetching for the same job before one of them can update the assignee
. You need to use the findAndModify
method instead: http://www.mongodb.org/display/DOCS/findAndModify+Command
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论