我试图通过MongoDB操作解决争用条件问题,这样就可以避免实现锁。
我的目标是能够检查集合中是否有任何具有特定状态的文档,如果没有,则将该状态设置为我选择的集合中的现有文档。该目标来自于这样一种需要,即由于并发性,我尝试的任何实现都会使我在竞争条件下更新多个文档。
让我举一个平行的例子,在并发调用的场景中,我的目标是逻辑检查是否没有任何现有的卡具有ACTIVE状态,我选择的卡被设置为ACTIVE,如果已经有ACTIVE卡,让它保持原样。所有这些都在一个MongoDB操作中完成。这个操作是由一个不同的流触发的,所以我可以't在执行该逻辑之前,不要更改流程以将卡设置为ACTIVE。
我尝试使用findOneAndUpdate
(甚至findOneAndReplace
)操作:
const query = {
userId: 'user-id-1',
status: 'ACTIVE',
};
Card.findOneAndUpdate(
query,
{
$set: {
_id: 'chosen-id', // A certain chosen ID to update if not found.
userId: 'user-id-1',
status: 'ACTIVE',
},
},
{
upsert: true,
returnNewDocument: true,
}
);
但我发现自己无法实现我的目标与任何可用的选项,因此我只实现更新找到的卡。如果没有找到,一切保持不变,我选择的卡没有设置为活动。唯一的情况是当添加像在示例upsert: true
我可以创建一个新的文档,但我不想,因为我想更新一个不同的现有文档。
文档示例:
{ "_id": "card-id-1", "userId": "user-id-1", "status": "READY" }
{ "_id": "card-id-2", "userId": "user-id-1", "status": "CANCELLED" }
{ "_id": "card-id-3", "userId": "user-id-1", "status": "READY" }
{ "_id": "card-id-4", "userId": "user-id-1", "status": "READY" }
在这种情况下,如果执行并选择card-id-4
作为回退(如果未找到ACTIVE),则预期结果应为:
{ "_id": "card-id-1", "userId": "user-id-1", "status": "READY" }
{ "_id": "card-id-2", "userId": "user-id-1", "status": "CANCELLED" }
{ "_id": "card-id-3", "userId": "user-id-1", "status": "READY" }
{ "_id": "card-id-4", "userId": "user-id-1", "status": "ACTIVE" }
如果再次执行,集合应该保持不变,因为card-id-4
是ACTIVE,所以操作正在查找一个集合。
MongoDB是否有任何实现可以在一次操作中实现这种行为?无论版本如何。如果需要,我对升级没有限制。
1条答案
按热度按时间qxgroojn1#
您可以在聚合管道中执行以下操作:
$facet
检查是否存在ACTIVE记录_id
设置为活动记录的_id
_id
设置为回退_id
_id
将构造的更新对象返回到集合默认情况下,单个文档级别上的操作是原子的,因此,您不需要太担心争用条件。
Mongo Playground when no active
Mongo Playground when there is active