If an error occurs during transaction, processing cannot continue
hokaccha opened this issue · comments
When handling a unique constraint error, such as the following, executing a query after a unique constraint error occurs will result in an error.
// user.ts
import { PrismaClient } from "@prisma/client";
/**
* Creates a user. Returns true if the creation succeeds or the user already exists.
*/
export async function ensureUser(
prisma: PrismaClient,
data: {
id: number;
name: string;
}
): Promise<boolean> {
try {
await prisma.user.create({ data });
return true;
} catch (err: any) {
const uniqConstraintFailed = err.code === "P2002";
if (uniqConstraintFailed) {
return true;
} else {
console.error(err);
return false;
}
}
}
// user.test.ts
test("ensureUser", async () => {
const data = { id: 1, name: "foo" };
// succeed
expect(await ensureUser(jestPrisma.client, data)).toBe(true);
// succeed
expect(await ensureUser(jestPrisma.client, data)).toBe(true);
// fail
expect(await jestPrisma.client.user.count()).toBe(1);
});
$ jest
FAIL ./app.test.ts
User
✕ ensureUser (16 ms)
● User › ensureUser
PrismaClientUnknownRequestError:
Invalid `jestPrisma.client.user.count()` invocation in
/path/to/app.test.ts:12:41
9 const data = { id: 1, name: "foo" };
10 expect(await ensureUser(jestPrisma.client, data)).toBe(true);
11 expect(await ensureUser(jestPrisma.client, data)).toBe(true);
→ 12 expect(await jestPrisma.client.user.count(
Error occurred during query execution:
ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(PostgresError { code: "25P02", message: "current transaction is aborted, commands ignored until end of transaction block", severity: "ERROR", detail: None, column: None, hint: None }), transient: false })
I suspect this is due to PostgresSQL's limitation that if an error occurs during a transaction, subsequent queries cannot be issued.
@hokaccha Thanks for your reporting.
I reproduced this message.
And I can suppress the 25P02
error using nested transaction like this:
/* user.test.ts */
test("ensureUser", async () => {
const data = { id: 1, name: "foo" };
// succeed
expect(await ensureUser(jestPrisma.client, data)).toBe(true);
try {
await jestPrisma.client.$transaction(async () => {
// succeed
expect(await ensureUser(jestPrisma.client, data)).toBe(true);
throw new Error();
});
} catch {}
expect(await jestPrisma.client.user.count()).toBe(1);
});
/* jest.config.mjs */
export default {
testEnvironment: "@quramy/jest-prisma-node/environment",
testEnvironmentOptions: {
enableExperimentalRollbackInTransaction: true, // Enable nested $transaction in test code
},
};
Here is full example: https://github.com/Quramy/jest-prisma-repro-141/pull/1/files .
And I can suppress the 25P02 error using nested transaction.
I see, it seems like this problem can be solved this way. Thank you.
As far as I'm concerned, it's okay to close this issue, but I'll leave the decision up to you 🙏
it's okay to close this issue, but I'll leave the decision up to you
Thanks. I'll write about this problem and the workaround to README's tips section.