Nestjs:如何使用mongoose启动会话进行交易?

Noo*_*ish 9 mongoose nestjs

使用事务的 mongoose 文档很简单,但是当在 Nestjs 中遵循它时,它会返回一个错误:

Connection 0 was disconnected when calling `startSession`
MongooseError: Connection 0 was disconnected when calling `startSession`
    at NativeConnection.startSession
Run Code Online (Sandbox Code Playgroud)

我的代码:

const transactionSession = await mongoose.startSession();
    transactionSession.startTransaction();

    try
    {
      const newSignupBody: CreateUserDto = {password: hashedPassword, email, username};
  
      const user: User = await this.userService.create(newSignupBody);

      //save the profile.
      const profile: Profile = await this.profileService.create(user['Id'], signupDto);

      const result:AuthResponseDto = this.getAuthUserResponse(user, profile);

      transactionSession.commitTransaction();
      return result;
    }
    catch(err)
    {
      transactionSession.abortTransaction();
    }
    finally
    {
      transactionSession.endSession();
    }
Run Code Online (Sandbox Code Playgroud)

Noo*_*ish 21

我在研究@nestjs/mongoose后找到了解决方案。这里的猫鼬与它没有任何联系。这就是返回错误的原因。

解决方案:

import {InjectConnection} from '@nestjs/mongoose';
import * as mongoose from 'mongoose';
Run Code Online (Sandbox Code Playgroud)

在服务类的构造函数中,我们需要添加服务可以使用的连接参数。

export class AuthService {
constructor(
  // other dependencies...
  @InjectConnection() private readonly connection: mongoose.Connection){}
Run Code Online (Sandbox Code Playgroud)

代替

const transactionSession = await mongoose.startSession();
transactionSession.startTransaction();
Run Code Online (Sandbox Code Playgroud)

我们现在将使用:

const transactionSession = await this.connection.startSession();
transactionSession.startTransaction();
Run Code Online (Sandbox Code Playgroud)

这样就可以解决startSession()后断线的问题了。


1Fp*_*x6k 5

除了 Noobish 提供的答案之外,我还想演示我在项目中使用的可重用函数:

import { ClientSession, Connection } from 'mongoose';

export const transaction = async <T>(connection: Connection, cb: (session: ClientSession) => Promise<T>): Promise<T> => {
  const session = await connection.startSession();

  try {
    session.startTransaction();
    const result = await cb(session);
    await session.commitTransaction();
    return result;
  } catch (err) {
    await session.abortTransaction();
    throw err;
  } finally {
    await session.endSession();
  }
}
Run Code Online (Sandbox Code Playgroud)

然后可以像这样使用它:

@Injectable()
export class MyService {
  constructor(
    @InjectModel(MyModel.name) private myModel: Model<MyModelDocument>,
    @InjectConnection() private connection: Connection,
  ) {}

  async find(id: string): Promise<MyModelDocument> {
    return transaction(this.connection, async session => {
      return this.myModel
        .findOne(id)
        .session(session);
    });
  }

  async create(myDto: MyDto): Promise<MyModelDocument> {
    return transaction(this.connection, async session => {
      const newDoc = new this.myModel(myDto);
      return newDoc.save({ session });
    });
  }
Run Code Online (Sandbox Code Playgroud)

显然,上面的示例仅用于演示目的,其中不需要事务,因为操作已经是原子的。但是,可以使用更复杂的示例来扩展内部回调,其中操作不是原子的,例如:

  1. 创建引用另一个架构文档的文档
  2. 查找引用的文档是否存在,如果存在,则将其添加到数组中(一对多关系)
  3. 如果引用的文档不存在,则抛出错误。这将中止事务并回滚所有更改,例如删除步骤 1 中创建的文档。需要session在所有这些步骤中仔细指定