Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TransactionRollbackError behavior isn't documented #269

Open
MarkSFrancis opened this issue Apr 9, 2024 · 5 comments
Open

TransactionRollbackError behavior isn't documented #269

MarkSFrancis opened this issue Apr 9, 2024 · 5 comments

Comments

@MarkSFrancis
Copy link

From the transaction docs, it appears that no error will be thrown by calling rollback() on a transaction, but doing so will throw a TransactionRollbackError. Should the docs be updated?

Documented behavior:
The code provided in the docs:

const db = drizzle(...)
await db.transaction(async (tx) => {
  const [account] = await tx.select({ balance: accounts.balance }).from(accounts).where(eq(users.name, 'Dan'));
  if (account.balance < 100) {
    await tx.rollback()
    return
  }
  await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
  await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
});

seems to be misleading, as the await before tx.rollback() is a no-op, and the return underneath will never be called. What will actually happen is that rollback() will throw an error, which is uncaught in this code example.

Actual behavior:

const db = drizzle(...)
await db.transaction(async (tx) => {
  const [account] = await tx.select({ balance: accounts.balance }).from(accounts).where(eq(users.name, 'Dan'));
  if (account.balance < 100) {
    // Throws a `TransactionRollbackError`
    tx.rollback();
  }
  await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
  await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
});

This suggests that throwing was unintentional behavior, but according to this issue in the drizzle-orm repo, it is intentional but undocumented.

@samyarkd
Copy link

samyarkd commented Jun 4, 2024

the docs are broke... this should be merged

@jadengis
Copy link
Contributor

I noticed this as well yesterday and I put up a fix for this here: #315

@hajek-raven
Copy link

When trying to use a rollback within a transaction, an exception is thrown. This makes it impossible to return a validation error from within the transaction flow while trying to rollback.

Consider this example:

const db = drizzle(...)
await db.transaction(async (tx) => {
  const [account] = await tx.select({ balance: accounts.balance }).from(accounts).where(eq(users.name, 'Dan'));
  if (account.balance < 100) {
      // This exception is caught by drizzle transaction block and rollback error is rethrown
      throw new TRPCError({
          code: "BAD_REQUEST",
          message: "Balance low"
      });
  }
  await tx.update(accounts).set({ balance: sql`${accounts.balance} - 100.00` }).where(eq(users.name, 'Dan'));
  await tx.update(accounts).set({ balance: sql`${accounts.balance} + 100.00` }).where(eq(users.name, 'Andrew'));
});

@jadengis
Copy link
Contributor

@hajek-raven throwing a normal error will also rollback the transaction so your example above will also work. You don't need to always manually rollback.

@hajek-raven
Copy link

hajek-raven commented Sep 19, 2024

@jadengis Yes, but how can I know the reason (original error) from the outside context?

try {
  await db.transaction(async (tx) => {
       throw new TRPCError({
          code: "BAD_REQUEST",
          message: "Wrong recipient"
      });
       
       // ...
       
       throw new TRPCError({
          code: "BAD_REQUEST",
          message: "Balance low"
      });
  });
} catch (e) {
 // e is always rollback error
}

The point is I want to show different messages to the user when different errors occur. But I always get Rollback error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants