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

Interaction between transactions and hooks #1072

Open
quentinhermiteau opened this issue Nov 27, 2024 · 3 comments
Open

Interaction between transactions and hooks #1072

quentinhermiteau opened this issue Nov 27, 2024 · 3 comments

Comments

@quentinhermiteau
Copy link

quentinhermiteau commented Nov 27, 2024

Package version

21.3.0

Describe the bug

Hello,

First of all, sorry if my english is not perfect, I'll try my best to be as understandable as possible.

My issue is the following, in my User model, I have a @afterCreate() that creates a Token, this token is sent by email to the user so that he can activate his account.

This worked perfectly until I introduced db transactions in the process.

Now my User is not created anymore by a simple User.create(), it is created in a transaction.
I need a transaction because when a user registers, i need to create multiple data in different tables and I wrapped this in a transaction so that i'm sure everything works well before the data is written in the database.

Now I added the transaction, the hook @afterCreate() throw an errror insert or update on table "tokens" violates foreign key constraint "tokens_user_id_foreign"

If I activate the debug so that I can see the SQL commands executed, I can see this:

CleanShot 2024-11-27 at 11 29 08@2x
CleanShot 2024-11-27 at 11 29 27@2x

We can see that the user is created, then the token, and then the await token.related('user').associate(user) tries to update the token to specify the user related to this token but it fails.

If i create the user outside a transaction, the issue disappear.

Here is an excerpt of my code

  • The transaction
await db.transaction(async (trx) => {
    const user = new User()
    user.fill({
      //...
    })

    user.useTransaction(trx)
    await user.save()

    const userProfile = new UserProfile()
    userProfile.fill({
        // ...
    })

    userProfile.useTransaction(trx)
    await userProfile.related('user').associate(user)
})
  • The @afterCreate():
@afterCreate()
static async createToken(user: User) {
    const token = await Token.create({
      id: randomUUID(),
      type: TokenType.activation,
    })

    await token.related('user').associate(user)
}

Maybe I just did something wrong, please let me know if so. Or maybe it's a bug and i'll be happy to help to solve this.

Thank you

Reproduction repo

No response

@thetutlage
Copy link
Member

Can you try the following in the hook?

const token = await Token.create({
  id: randomUUID(),
  type: TokenType.activation,
}, { client: user.$trx }) // 👈 Notice this change

await token.related('user').associate(user)

@quentinhermiteau
Copy link
Author

quentinhermiteau commented Nov 27, 2024

Thank you for your quick response! It solves my problem, thank you for your help.
And what happens now if the hook is call by a User creation but not in a transaction? The user.$trx might be undefined

@thetutlage
Copy link
Member

The user.$trx might be undefined

Yes and that is not the problem. What was happening earlier was, you were trying to create the token outside of the transaction and assigning to the user within the transaction.

Another way to fix this is to use the user -> hasMany -> tokens relationship for persistence and then you won't need this user.$trx assignment. Something like

await user.related('tokens').create({
  id: randomUUID(),
  type: TokenType.activation
})

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

2 participants