我们需要在网站模型下存储多个Feed,如下所示:
{
id: site_id
name: site_name
feeds: [
{
url: feed_url_1
date: feed_update_date_1
},
{
url: feed_url_2
date: feed_update_date_2
},
...
]
}
由于feeds
是一个数组,我们可以使用$set
,$push
或$addToSet
对其进行更新。
当我们的并发应用程序(队列)尝试更新同一站点模型时,可能会发生2种不同的竞争条件(写入偏斜)。
如果我们选择$set
,并在客户端保护副本,那么如果2个队列正在写入同一站点,则可能会丢失一个源,但序列如下。
Given a wordpress site, extract 2 feeds (RSS and ATOM), dispatch to Q1 and Q2.
Q1: load existing feed, check RSS feed is new
Q2: load existing feed, check ATOM feed is new
Q1: $set feeds => [RSS]
Q2: $set feeds => [ATOM]
现在RSS Feed已丢失。
如果我们选择$push
或$addToSet
,则可能会发生以下情况。
User A added a site, putting RSS feed to Q1
User B added the same site, putting the same RSS feed to Q2
Q1: load existing feed, check RSS feed is new
Q2: load existing feed, check RSS feed is new
Q1: $push RSS
Q2: $push RSS
现在RSS Feed已被复制
如果我们的数据模型只是{ url }
,那么$addToSet
将防止重复投放。但不幸的是,情况并非如此,date
属性可能会有所不同。因此$addToSet
并不比$push
安全得多。
我们已经想到了这个问题的一些可能的解决方法,但鉴于我们的时间紧迫,没有一个是好的。
将Feed从网站分离到自己的集合中,单独使用url
进行保护,并相应地更改我们的模型和存储库。
首先将部分{ url }
插入网站模型,然后使用其他信息更新它们,这会使$addToSet
可用,但可能会破坏需要date
的其他队列永远存在(需要测试)。
让竞争条件按原样发生,$push
首先使用背景,使用后台队列检测重复并稍后将其删除。
(如果upsert与位置查询一起工作,可能会有第4个解决方案,但据我所知MongoDB v2.4还没有它)
所以我想知道是否有更好的选择来解决这种竞争条件。或者,如果有一些最佳实践。
答案 0 :(得分:4)
你可能想看看tokumx,一个支持transactions的mongodb分支(除了一些其他有用的东西)
答案 1 :(得分:2)
您可以在更新选择器上使用gard:
alice(mongod-2.4.8) test> db.foo.save({_id: 12 })
Updated 1 new record(s) in 1ms
alice(mongod-2.4.8) test> db.foo.update({ _id: 12, "feeds.url" : {$ne: "baz"} },
{ $push : { feeds : { url: "baz" } } } )
Updated 1 existing record(s) in 1ms
alice(mongod-2.4.8) test> db.foo.update({ _id: 12, "feeds.url" : {$ne: "baz"} },
{ $push : { feeds : { url: "baz" } } } )
Updated 0 record(s) in 1ms
alice(mongod-2.4.8) test> db.foo.find({_id: 12 })
{
"_id": 12,
"feeds": [
{
"url": "baz"
}
]
}
Fetched 1 record(s) in 1ms -- Index[_id_]