Multithreading 如何确保两个用户能够自动确认mongodb中发生的事务

Multithreading 如何确保两个用户能够自动确认mongodb中发生的事务,multithreading,mongodb,transactions,mongodb-query,Multithreading,Mongodb,Transactions,Mongodb Query,我有一个名为事务的模型,它具有以下模式 var transactionSchema = new mongoose.Schema({ amount: Number, status: String, _recipient: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, _sender: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, });

我有一个名为事务的模型,它具有以下模式

var transactionSchema = new mongoose.Schema({
    amount: Number,
    status: String,
    _recipient: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    _sender: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
});
我希望此交易的发送方和接收方都能够“确认”交易已发生。
状态开始时为“初始”。因此,当只有发件人确认了交易(但收件人尚未确认)时,我想将
状态更新为“senderConfirmed”或其他什么,当收件人确认了交易(但发件人未确认)时,我想将状态更新为“recipientConfirmed”。当他们都确认后,我想将状态更新为“完成”

问题是,我如何知道何时以避免比赛条件的方式将其更新为“完成”?如果发送方和接收方同时确认交易,那么两个线程都会认为状态为“初始”,并将其更新为“发送方确认”或“接收方确认”,而实际上它应该转到“完成”


我读过MongoDBs两阶段提交方法,但这不太适合我的需要,因为我不想(在另一个线程当前正在修改事务的情况下)阻止第二个线程进行更新-我只想让它等到第一个线程完成后再进行更新,然后根据交易的最新状态确定其更新内容。

更新:我正在根据一条评论澄清我的答案,我的原始回复没有提供答案

另一种方法是将状态作为两个单独的属性进行跟踪:

senderConfirmed: true/false,
recipientConfirmed: true/false,
当发件人确认后,您只需更新
senderConfirmed
字段。当收件人确认后,您将更新
recipientcomfixed
字段。他们不可能互相覆盖

要确定事务是否完成,只需查询
{senderConfirmed:true,recipientConfirmed:true}

显然,这是对文档模式的更改,因此可能并不理想

原始答案

是否可以更改您的模式?如果您有两个属性-
senderStatus
recipientStatus
?发件人将只更新
senderStatus
,收件人将只更新
recipientStatus
。然后他们无法覆盖彼此的更改

我想,你还需要其他方法来标记它是否完整。你可以给我们找个老太婆之类的工作

底线是您需要“两个”update语句来分别为发送方和接收方执行此操作。因此,基本上一个将尝试将“部分”状态设置为完成,另一个将仅将“初始”状态匹配设置为“部分”状态

批量操作是实现多个语句的最佳方式,因此您应该通过访问底层驱动程序方法来使用这些语句。现代API发行版有这种方法,如果服务器版本不支持“批量”协议,这种方法会很好地降低性能,而只是退回到发布单独的更新

//发送方确认
Transaction.collection.bulkWrite(
[
{“updateOne”:{
“过滤器”:{
“_id”:docId,
“_sender”:senderId,
“状态”:“收件人已确认”
},
“更新”:{
“$set”:{“状态”:“完成”}
}
}},
{“updateOne”:{
“过滤器”:{
“_id”:docId,
“_sender”:senderId,
“状态”:“初始”
},
“更新”:{
“$set”:{“状态”:“发件人已确认”}
}
}}
],
{“有序”:false},
函数(错误、结果){
//结果将确认最多只有一次更新成功
}
);
当然,除了不同的状态检查或更改之外,这同样适用于
\u收件人。您可以交替对
\u发送方
\u接收方
发出
$或
条件,并具有通用的“部分”状态,而不是编码不同的更新条件,但相同的基本“两次更新”过程适用

当然,您“可以”再次使用常规方法,并以另一种方式向服务器发出两个更新,甚至可能并行,因为条件仍然是“原子的”,但这也是使用
{“ordered”:false}
选项的原因,因为这里不需要考虑它们的确定顺序

不过,批量操作比单独调用要好,因为发送和返回的请求和响应只有一个,而不是两个,因此使用批量操作的开销要小得多

但这是一般的做法。在另一方也发出确认之前,任何一份声明都不可能在“僵局”中留下“状态”或标记为“完成”

在第一次尝试更新和第二次尝试更新之间,状态从“初始”更改为“可能”,这将导致没有任何更新。在这种情况下,您可以“重试”在后续尝试中“应该”更新的操作

但这最多只需要“一次”重试。而且非常罕见


注意:在Mongoose车型上使用
.collection
存取器时应小心。所有常规模型方法都内置了逻辑来“确保”到数据库的连接在它们做任何事情之前实际上是存在的,事实上,在连接存在之前“排队”操作

通常,最好将应用程序启动包装在事件处理程序中,以确保数据库连接:

mongoose.on(“打开”,函数(){
//应用程序启动和初始化在这里
})
因此,在本例中使用“
上的
”或“
一次”
事件

一般来说,尽管连接始终存在,但eit