我们有一个需要在站点模型下存储多个数据源的场景,具体如下:
{
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个队列正在写入同一站点,则一个feed可能会丢失,具体如下:
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源已经丢失。
如果我们选择$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提要已经被复制了。
如果我们的数据模型只是{url},那么$addToSet将保护免受重复提要的影响。但不幸的是,情况并非如此,date属性可能不同。所以$addToSet并没有比$push更安全。
我们考虑了一些可能的解决方法,但是考虑到我们紧张的时间表,都不是很好。
1.将提要从网站中分离出来成为一个独立的集合,仅用url进行保护,并相应地更改我们的模型和存储库。
2.首先向网站模型插入部分{url},然后使用其他信息更新它们,这应该可以使$addToSet可用,但可能会破坏需要始终存在日期的其他队列(需要测试)。
3.让竞争条件发生,先$push提要,然后使用后台队列检测重复项并稍后删除它们。
(如果我所知道的MongoDB v2.4还没有位置查询的upsert,那么可能有第4个解决方案)
因此,我想知道是否有更好的替代方案来解决这种竞争条件。或者是否有一些最佳实践。