GOOGLE ADS

martes, 3 de mayo de 2022

La transacción de MongoDB con @NestJs/mongoose no funciona

En serio necesito tu ayuda. Mi transacción de MongoDB con @NestJs/mongoose no funciona... Cuando falla el pago de mi franja, la reversión no funciona... Sin embargo, mi colección de pedidos guardó los datos... ¿Cómo puedo solucionar este problema...?

 async create(orderData: CreateOrderServiceDto): Promise<any> {
const session = await this.connection.startSession();
session.startTransaction();
try {
const createOrder = new this.orderModel(orderData);
const order = await createOrder.save();
await this.stripeService.charge(
orderData.amount,
orderData.paymentMethodId,
orderData.stripeCustomerId,
);
await session.commitTransaction();
return order;
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
await session.endSession();
}
}


Solución del problema

Tuve el mismo problema y lo encontré en github: Mongo DB Transactions With Mongoose & Nestjs

Entonces creo que, de acuerdo con este problema, debe llamar al createmétodo de su modelo, así:

const order = await this.orderModel.create(orderData, { session });

como puede ver, el Model.createmétodo tiene una sobrecarga con SaveOptionscomo parámetro:

create(docs: (AnyKeys<T> | AnyObject)[], options?: SaveOptions): Promise<HydratedDocument<T, TMethodsAndOverrides, TVirtuals>[]>;

SaveOptionstoma un parámetro opcional que puede contener la sesión:

interface SaveOptions {
checkKeys?: boolean;
j?: boolean;
safe?: boolean | WriteConcern;
session?: ClientSession | null;
timestamps?: boolean;
validateBeforeSave?: boolean;
validateModifiedOnly?: boolean;
w?: number | string;
wtimeout?: number;
}

Tenga en cuenta que Model.save()también puede tomar un SaveOptionsparámetro. Entonces también puedes hacer lo que hiciste así:

const createOrder = new this.orderModel(orderData);
const order = await createOrder.save({ session });

Un poco mas lejos...

Como hago muchas cosas que requieren una transacción, se me ocurrió este ayudante:

import { InternalServerErrorException } from "@nestjs/common"
import { Connection, ClientSession } from "mongoose"
export const mongooseTransactionHandler = async <T = any>(
method: (session: ClientSession) => Promise<T>,
onError: (error: any) => any,
connection: Connection, session?: ClientSession
): Promise<T> => {
const isSessionFurnished = session === undefined? false: true
if (isSessionFurnished === false) {
session = await connection.startSession()
session.startTransaction()
}
let error
let result: T
try {
result = await method(session)
if (isSessionFurnished === false) {
await session.commitTransaction()
}
} catch (err) {
error = err
if (isSessionFurnished === false) {
await session.abortTransaction()
}
} finally {
if (isSessionFurnished === false) {
await session.endSession()
}
if (error) {
onError(error)
}
return result
}
}

the optional parameter session is in case you are doing nested nested transaction.
for example: you delete a User model, and then the user's avatar which is a File model.

/** UserService **/
async deleteById(id: string): Promise<void> {
const transactionHandlerMethod = async (session: ClientSession): Promise<Order> => {
const user = await this.userModel.findOneAndDelete(id, { session })
await this.fileService.deleteById(user.avatar._id.toString(), session)
}
const onError = (error: any) => {
throw error
}
await mongooseTransactionHandler<Order>(
transactionHandlerMethod,
onError,
this.connection
)
}
/** FileService **/
async deleteById(id: string, session?: ClientSession): Promise<void> {
const transactionHandlerMethod = async (session: ClientSession): Promise<Order> => {
await this.fileModel.findOneAndRemove(id, { session })
}
const onError = (error: any) => {
throw error
}
await mongooseTransactionHandler<Order>(
transactionHandlerMethod,
onError,
this.connection,
session
)
}

Entonces, en resumen:

You can use it like this:

async create(orderData: CreateOrderServiceDto): Promise<any> {
const transactionHandlerMethod = async (session: ClientSession): Promise<Order> => {
const createOrder = new this.orderModel(orderData);
const order = await createOrder.save({ session });
await this.stripeService.charge(
orderData.amount,
orderData.paymentMethodId,
orderData.stripeCustomerId,
);
return order
}
const onError = (error: any): void => {
throw error
}
const order = await mongooseTransactionHandler<Order>(
transactionHandlerMethod,
onError,
this.connection
)
return order
}

Espero que ayude.

No hay comentarios:

Publicar un comentario

Regla de Firestore para acceder a la generación de subcolección Permisos faltantes o insuficientes

Tengo problemas con las reglas de Firestore para permitir el acceso a algunos recursos en una subcolección. Tengo algunos requests document...