不允许 Mongoose / mongoDB 使用过期的会话

Mongoose / mondoDB Use of expired sessions is not permitted

我正在尝试与 mongoose 进行交易。

当我 运行 代码时,看起来 mongoose 文档甚至在调用 await session.endSession() 之后还记得会话。后来,当我对该文档调用 .save() 时,出现错误“不允许使用过期会话”。

这是演示(只是一个简化的例子):

const MessageSchema = new mongoose.Schema({ text: String }, {strict: 'throw'});
const Message = mongoose.model('Message', MessageSchema);

const session = await mongoose.startSession();

let message;

await session.withTransaction(async () => {
    message = (await Message.create([{text: 'My message'}], {session}))[0];
});

await session.endSession();

message.text = 'My message 2';
await message.save();

最后的 .save() 抛出了那个错误。如果你记录 message.$session().hasEnded 你会得到 true。我的期望是,如果会话结束,.save() 最好不要使用它。

我想达到的目标: 使用事务创建一些文档,提交它们,所以它们在数据库中。

稍后,使用数据库中已有的同一文档对其进行一些更改。

我在这里做错了什么?如何防止 .save() 抛出错误并尝试使用过期的会话?

mongoose docs 中的任何地方都没有关于 endSession 的部分,但是正如您自己发现的那样,当调用此函数时,它只会为您的 [=12= 设置一个标志] 对象而不是销毁它。如果它被销毁,您将有另一个错误。

My expectation was that, if session has ended, .save() would be smart not to use it.

事实上,它足够聪明,不会使用它,框架只会通知您您正在尝试非法指令。

What I want to achieve: Create some documents with transaction, commit them, so they are in the database.

这正是您对 withTransaction 调用所做的。 This wrapper 帮助您创建、提交和 abort/retry 以防万一发生不好的事情,所以在那个调用之后,如果一切顺利,您确实成功地在数据库中创建了一个文档。

Later, use that same document, that is already in the database, to make some changes to it.

取决于你所说的“以后”是什么意思。如果它是同一个端点的一部分(而且我不知道为什么你会立即修改该文档而不是首先正确地提交它),那么正如我在评论中所说的那样,移动 endSession 调用很可能解决问题:

const MessageSchema = new mongoose.Schema({ text: String }, {strict: 'throw'});
const Message = mongoose.model('Message', MessageSchema);

const session = await mongoose.startSession();

let message;

await session.withTransaction(async () => {
    message = (await Message.create([{text: 'My message'}], {session}))[0];
});

message.text = 'My message 2';
await message.save();

await session.endSession();

如果它是另一个端点的一部分,那么只需像刚才那样进行另一个事务,只是修改文档而不是创建它。或者,如果您根本不需要交易,请使用 findOneAndUpdatefindByIdAndUpdate 之类的方法。我认为您似乎对 JS 和 Mongoose 足够熟悉,可以自己完成。

What am I doing wrong here?

基本上,不多。您已经了解结束会话后无法调用 .save()

How can I prevent .save() from throwing an error and trying to use expired session?

你不能。它只是提醒您正在尝试进行非法操作。但是,您可以 try/catch 错误并决定在 catch 子句中对此不做任何处理,或者编写一个 if 语句检查 message.$session().hasEnded.