Skip to content

Conversation

@mukunda-
Copy link

@mukunda- mukunda- commented Feb 4, 2025

Description of change

Rearrange the query function in the MySQL query runner to build the error outside of the query callback scope, so it includes the caller's stack trace.

The inner stack trace, if desired, is still available via the wrapped driverError.

Functionality wise, this is a cosmetic change for diagnostics. It doesn't change program behavior, only stack trace logs. Only MySQL and equivalent flavors are in the blast radius here.

See #11271 for more details. Currently, when there's an query error while using mysql, it's hard to tell where it's coming from since the stack doesn't show the caller. (The "trace" option of the MySQL driver also does not reach outside of the typeorm code.) Working with traces like these is frustrating:

{8A435313-F939-409E-80F2-2D52F9B1FCF5}

Fixes #11271
Fixes #11765

Pull-Request Checklist

  • Code is up-to-date with the master branch
  • npm run format to apply prettier formatting
  • npm run test passes with this change
  • This pull request links relevant issues as Fixes #0000
  • There are new or updated unit tests validating the change
  • Documentation has been updated to reflect this change -- N/A
  • The new commits follow conventions explained in CONTRIBUTING.md

Rearrange the `query` function in the MySQL query runner to build the error outside of the query callback scope, so it includes the caller's stack trace.

The inner stack trace, if desired, is still available via the wrapped `driverError`.

Closes: typeorm#11271
@coveralls
Copy link

Coverage Status

coverage: 72.391% (+0.02%) from 72.369%
when pulling b0c7f5d on mukunda-:fix-mysql-stack-trace
into 79960e1 on typeorm:master.

@mukunda-
Copy link
Author

mukunda- commented Feb 5, 2025

Example of what I see when testing the PR at hand:

Image

Much easier to see that I missed passing the transaction manager to the audit log service.

Comment on lines 67 to 86
try {
const hygge = repository.create()
hygge.description =
"Soft blankets and candlelight on a snowy evening."
await repository.save(hygge)

const hygge2 = repository.create()
hygge2.id = hygge.id // Id collision.
hygge2.description =
"Eating homemade simply wonderfuls."
await repository.insert(hygge2)
} catch (err) {
//console.log("observed stack:", err.stack)
expect(err.stack).to.include("my_named_function_1234")
confirmedStackError = true
} finally {
// Confirm that we actually checked an error.
expect(confirmedStackError).to.equal(true)
}
}
Copy link

@klingenm klingenm Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could improve the readability of the test by separating the assertions from the tested code. Also applies on the previous test.

Suggested change
try {
const hygge = repository.create()
hygge.description =
"Soft blankets and candlelight on a snowy evening."
await repository.save(hygge)
const hygge2 = repository.create()
hygge2.id = hygge.id // Id collision.
hygge2.description =
"Eating homemade simply wonderfuls."
await repository.insert(hygge2)
} catch (err) {
//console.log("observed stack:", err.stack)
expect(err.stack).to.include("my_named_function_1234")
confirmedStackError = true
} finally {
// Confirm that we actually checked an error.
expect(confirmedStackError).to.equal(true)
}
}
const hygge = repository.create()
hygge.description =
"Soft blankets and candlelight on a snowy evening."
await repository.save(hygge)
const hygge2 = repository.create()
hygge2.id = hygge.id // Id collision.
hygge2.description =
"Eating homemade simply wonderfuls."
await repository.insert(hygge2)
}
expect(my_named_function_1234()).rejectsToThrow('my_named_function_1234')

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Updated the test.

Figuring out chai promises is a bit of a rabbithole... seems the normal rejectedWith doesn't check the stack text.
await expect(my_named_function_1234()).to.be.rejected.and.eventually.have.property("stack").that.include("my_named_function_1234")

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi @mukunda- would you like to continue working on this? We are considering dropping mysql suppprt in favour of mysql2 and this seems to be important part of this move...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @pkuczynski Sorry I'm no longer working on the project that needed these changes so I don't have time for this lately. If someone else could pick this up that would be great

Clean up test structure.
@JingYeoh
Copy link

JingYeoh commented Feb 7, 2025

same issue, i have to restart server, this issue blocked my server, i insert a data to a table, no error, and the db table is right, but this error happened, no error stack, how to resolve this issue?

const queryStartTime = +new Date()
const queryStartTime = +new Date()
return await new Promise<any>((ok, fail) => {
databaseConnection.query(
Copy link
Collaborator

@alumni alumni Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To get rid of the promise wrapper, you can do this:

const queryAsync = promisify(databaseConnection.query).bind(databaseConnection)

const raw = await queryAsync(query, parameters)

promisify is a NodeJS built-in, but it has a simple implementation, which we could add to util. We could even create ObjectUtils.promisifyMethod(obj, method) that does obj[method].call(obj, ...args, cb), so it would already be bound to the object. 🤷🏻 There's a JS example (without types) in node_modules/@sap/hana-client/extension/Promise.js 😉

Then you won't need any callbacks :)

If we want to catch exceptions in an async function, we need to await it (this was the fix, wasn't it?). This is arguably more obvious when functions are used instead of promises 😉

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I don't have much time currently to update and test but it will be on my todo list somewhere

Copy link
Collaborator

@alumni alumni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I see we do the same in all drivers, I wonder if we shouldn't adjust the logger instead.

Copy link
Collaborator

@gioboa gioboa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you solve the conflicts please?

Copy link
Collaborator

@gioboa gioboa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I ask you to solve the conflicts please?
Thanks for your help @mukunda-

Copy link
Collaborator

@gioboa gioboa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @mukunda- for your help
is this PR still valid? May I ask you to solve the conflicts ?

Copy link
Collaborator

@gioboa gioboa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mukunda- thanks for opening this PR.
I'm closing this for now because it's stale and has conflicts, feel free to create a new PR with your improvements. 💪

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

how to print real location in code, when the typeorm throw error Stack trace is truncated in MySQL query runner

7 participants