我们正在为我们的系统建立规模计算机制。
为了计算大小,我们从第一个原子操作开始 - findAndModify
- 找到对象并向其添加锁属性(以防止该对象的另一个计算与它交互并等到最后,因为我们可能有许多并行计算 - 在这种情况下其他应该推迟),然后我们计算特定属性的大小,并在此操作后 - 我们添加元数据到对象和删除锁。
但是,似乎有时候,当我们对单个对象进行大量多次计算时(特别是当我们并行计算大量对象时),某些更新不会被执行。
_size
元数据如下所示:
{
_lockedAt: SomeDate,
_transactionId: 'abc'
}
计算后应该如下所示:
{
somePropertySize: 123,
anotherPropertySize: 1245,
(...)
_total: 131431523 // Some number
// Notice that both _lockedAt and _transactionId should be missing
}
这就是我们的更新流程的样子:
return Promise.coroutine(function * () {
yield object.findOneAndUpdate({
'_id': gemId,
'_size._lockedAt': {
$exists: false
}
}, {
$set: {
'_size._lockedAt': moment.utc().toDate(),
'_size._transactionId': transactionId
}
}).then(results => results.value);
// Calculations are performed here, new _size object is built
yield object.findOneAndUpdate({
_id: gemId,
_lockedAt: {
$exists: true // We tried both with and without this property, does not change anything
}
}, {
$set: {
_size: newSizeObject
}
});
})()
第二次更新前的示例性现实对象(为简洁而截断):
{
title: 11,
description: 2,
detailedSection: 0,
tags: 2
file: 5625898,
_total: 5625913
}
出于某种原因,当我们彼此相邻时有多个计算时,有时(对于新对象,根本没有_size
属性),对象与_size
对象保持完全一样,锁定后,尽管事实日志告诉我们一切顺利(计算完成,计算新的大小对象并调用第二个DB更新)。
我们使用MongoDB 3.0,两个replicaSet。关于发生了什么的任何想法?
答案 0 :(得分:2)
在第二次更新之后放置,然后它将等到promise解决:
object.findOneAndUpdate({
'_id': gemId,
'_size._lockedAt': {
$exists: false
}
}, {
$set: {
'_size._lockedAt': moment.utc().toDate(),
'_size._transactionId': transactionId
}
}).then(results => {
// Calculations are performed here, new _size object is built
object.findOneAndUpdate({
_id: gemId,
_lockedAt: {
$exists: true // We tried both with and without this property, does not change anything
}
}, {
$set: {
_size: newSizeObject
}
});
}).catch(err => console.error);
还要确保使用catch对您的承诺进行错误处理。
如果你真的不需要锁定或交易字段,那么我会删除那些东西。如果你确实需要它们,像RethinkDB这样的东西可能会更好一些,或者PostgresSQL可以提供真正的交易。
答案 1 :(得分:0)
总而言之,我仔细检查了代码并且实际发生了什么,事实上代码的完全不同部分是从数据库中查询对象然后,在一些其他操作(包括我的)之后,它将对象写入数据库(因此,覆盖我的更改)。
因此,对每个MongoDB用户来说都很重要 - 请记住MongoDB不是事务性的,但仍然是 atomic ,这意味着它可以保证您的操作将被持久化,但不保证数据操作之间将持续存在。
总结一下,我通过这个例子学到的东西:
$set
,$inc
,$unset
和其他特殊运营商。如果你有很多参数,请使用例如mongo-dot-notation
npm库将您的数据展平为$set
选择器。