Pantheon
Pantheon
KKysely
Created by Pantheon on 9/10/2024 in #help
Roll back transaction prematurely
I managed to solve it with a wrapper function. Here it is, for anyone who may need it in the future:
/**
* Wraps a Kysely transaction such that any `Err` returned from the callback results in a rollback.
* Any exceptions thrown will result in an `Err<"UnknownTransactionError">`.
* @param db The Kysely database instance.
* @param cb The transaction handler which returns a `Result`.
* @returns The Result from the transaction callback, or an `Err<"UnknownTransactionError">`.
*/
export default async function transactResult<V, E extends string>(
db: Kysely<Tables>,
cb: (trx: Transaction<Tables>) => Promise<Result<V, E>>
): Promise<Result<V, E | "UnknownTransactionError">> {
class InvokedRollback extends Error {
constructor(public err: Err<E>) {
super();
}
}

try {
const transactionOkValue = await db.transaction().execute(async trx => {
const returnValue = await cb(trx);
if (returnValue.isErr()) {
logger.error("Transaction handler returned an Err:", returnValue.error);

// make Kysely rollback this transaction
throw new InvokedRollback(returnValue);
}

return returnValue;
});

return transactionOkValue;
} catch (error) {
if (error instanceof InvokedRollback) {
logger.error("Transaction rolled back.");
return error.err as Err<E>;
} else {
// Another unknown error thrown inside the transaction handler.
logger.error(
"Unknown error thrown inside transaction handler. This may indicate a missing Err check:",
error
);
return new Err("UnknownTransactionError");
}
}
}
/**
* Wraps a Kysely transaction such that any `Err` returned from the callback results in a rollback.
* Any exceptions thrown will result in an `Err<"UnknownTransactionError">`.
* @param db The Kysely database instance.
* @param cb The transaction handler which returns a `Result`.
* @returns The Result from the transaction callback, or an `Err<"UnknownTransactionError">`.
*/
export default async function transactResult<V, E extends string>(
db: Kysely<Tables>,
cb: (trx: Transaction<Tables>) => Promise<Result<V, E>>
): Promise<Result<V, E | "UnknownTransactionError">> {
class InvokedRollback extends Error {
constructor(public err: Err<E>) {
super();
}
}

try {
const transactionOkValue = await db.transaction().execute(async trx => {
const returnValue = await cb(trx);
if (returnValue.isErr()) {
logger.error("Transaction handler returned an Err:", returnValue.error);

// make Kysely rollback this transaction
throw new InvokedRollback(returnValue);
}

return returnValue;
});

return transactionOkValue;
} catch (error) {
if (error instanceof InvokedRollback) {
logger.error("Transaction rolled back.");
return error.err as Err<E>;
} else {
// Another unknown error thrown inside the transaction handler.
logger.error(
"Unknown error thrown inside transaction handler. This may indicate a missing Err check:",
error
);
return new Err("UnknownTransactionError");
}
}
}
9 replies
KKysely
Created by Pantheon on 9/10/2024 in #help
Roll back transaction prematurely
The reason for all this is I'm trying to make transactions play nice with the error handling pattern from https://github.com/JohannesKlauss/ts-results-es
9 replies
KKysely
Created by Pantheon on 9/10/2024 in #help
Roll back transaction prematurely
Yeah, that was a mistake left over from before starting the refactor
9 replies