我有一个文档结构,用于存储每个用户设备的应用程序相关数据。该公司有一些可用的应用程序,这些应用程序是有限的,并且不会经常更改。所以我使用嵌入式子文档数组方法设计了文档,以减少搜索并使其适用于聚合管道。
定义一个文件:
{
_id: "device_unique_identification_code",
device_model: "iPhone5,2",
applications: [
{
app_id: "a_game",
push_token: "edbca9078d6c0c3a9f17166bbcf5be440c8ed2c6",
last_user_id: 132522
},
{
app_id: "an_app",
push_token: "fed949982ceac84279f22a29bdd66cc13b7750e1",
last_user_id: 132522
},
{
app_id: "yet_another_game",
push_token: "5cbf5a2bf0db7d6d55bd454222844d37d4f351b6",
last_user_id: 842452
},
{
app_id: "yet_another_app",
push_token: "d1b60db7d54246d55bd37f4f35d45c2284b5a2bf",
last_user_id: 842452
}
]
}
此集合仅存储特定于设备应用的数据,所有与会话/用户相关的数据都保存在另一个集合中。
由于这些应用程序非常繁忙,我必须使用原子命令来做任何事情来降低竞争条件的风险。
以下是问题。
给定设备" a"和应用程序" b",将一些设备应用程序值(例如,保存push_token)存储在单个原子命令中。
以下是测试用例。
我浪费了几天尝试使用upsert / addToSet / setOnInsert / etc进行各种查询。但仍然没有线索。
PS。我还有两个选择。
答案描述
解决方案是在版本字段中使用乐观锁定。
previousVersion = 0
while (true) {
// find target document with current version number,
// also create a new document with version 1 initially
// also find whether there is an existing app_id
// so we don't have to loop through the array
doc = db.devices.findAndModify({
query:{_id:"given_device_id"},
update:{$setOnInsert:{version:1}},
fields:{version:1,applications:{$elemMatch:{app_id:"given_app_id"}}},
upsert:true,
new:true})
// prevent unexpected infinite loop
if (previousVersion == doc['version']) {
throw new InfiniteLoopExpectedException()
}
previousVersion = doc['version']
if (doc contains applications) {
// if document contains the target application
// update it using $ positioning because I am too lazy to find the index
result = db.devices.update(
{
_id:"given_device_id",
version:doc['version'],
"applications.app_id":"given_app_id"
},
{
$inc:{version:1},
$set:{"applications.$.push_token":"given_value"}
})
} else {
// no app_id found ? simply push
result = db.devices.update(
{_id:"given_device_id",version:doc['version']},
{
$inc:{version:1},
$push:{applications:{app_id:"given_app_id",push_token:"given_value"}}
})
}
// if the update command failed, retry the process again
if (result['nModified'] == 1) {
break
}
}
答案 0 :(得分:1)
你试过find and modify吗?
如果添加字段version
,则可以确保仅根据查询(版本)中的记录版本进行更新。如果没有通过,您可以重新阅读文档(获取版本n + m),增加版本(n + m + 1),重新应用您的更改并重试findandmodify
匹配您刚刚阅读的版本号(N + M)。最终你会成功,当你知道在你的阅读和写作之间没有其他任何线程或过程介入。