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

Support unique constraint exceptions when running Vitess. #3386

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

rgfindl
Copy link
Contributor

@rgfindl rgfindl commented Mar 19, 2025

Description

misk-hibernate didn't properly handle ConstraintViolationExceptions when running with Vitess.

After the retries are complete, the ConstraintViolationException should be thrown, but in the case of Vitess, a session.target(previous) throws a GenericJDBCException because the connection is already closed.

This PR catches the GenericJDBCException and ignores it.

Testing Strategy

Update the existing test to include Vitess.

  • I have reviewed this PR with relevant experts and/or impacted teams.
  • I have added tests to have confidence my changes work as expected.
  • I have a rollout plan that minimizes risks and includes monitoring for potential issues.

Thank you for contributing to Misk! 🎉

transacter.transaction { session ->
session.save(DbMovie("Cinderella", LocalDate.of(1950, 3, 4)))
}
assertFailsWith<ConstraintViolationException> {
transacter.transaction { session ->
session.save(DbMovie("Beauty and the Beast", LocalDate.of(1991, 11, 22)))
session.save(DbMovie("Cinderella", LocalDate.of(2015, 3, 13)))
repeat(5) { // Repeat 5 times to trigger Vitess constraint on a single shard
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should something in the stack be doing these retries automatically?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was just to ensure we get at least two writes on a single shard. The unique constraint is limited to each shard.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

NVM, a single insert causes the constraint violation. Not sure why I needed additional inserts before.

Copy link
Collaborator

@adrw adrw left a comment

Choose a reason for hiding this comment

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

LG

use(previous)
try {
use(previous)
} catch (th: GenericJDBCException) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
} catch (th: GenericJDBCException) {
} catch (exception: GenericJDBCException) {

use(previous)
} catch (th: GenericJDBCException) {
// Ignore the exception if the connection is already closed.
if (!isConnectionClosed(th)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (!isConnectionClosed(th)) {
if (!isConnectionClosed(exception)) {

} catch (th: GenericJDBCException) {
// Ignore the exception if the connection is already closed.
if (!isConnectionClosed(th)) {
throw th
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
throw th
throw exception

}
} else {
function()
}
}

private fun isConnectionClosed(th: GenericJDBCException) =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
private fun isConnectionClosed(th: GenericJDBCException) =
private fun isConnectionClosed(exception: GenericJDBCException) =

}
} else {
function()
}
}

private fun isConnectionClosed(th: GenericJDBCException) =
th.cause?.javaClass == SQLException::class.java &&
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
th.cause?.javaClass == SQLException::class.java &&
exception.cause?.javaClass == SQLException::class.java &&

}
} else {
function()
}
}

private fun isConnectionClosed(th: GenericJDBCException) =
th.cause?.javaClass == SQLException::class.java &&
th.cause?.message.equals("Connection is closed")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
th.cause?.message.equals("Connection is closed")
exception.cause?.message.equals("Connection is closed")

@@ -368,9 +367,6 @@ abstract class TransacterTest {

@Test
fun constraintViolationCausesTransactionToRollback() {
// Uniqueness constraints aren't reliably enforced on Vitess
assumeTrue(!transacter.config().type.isVitess)
Copy link
Collaborator

Choose a reason for hiding this comment

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

If this test is run on non-Vitess databases, should it stay and within an if block that stops it from running for Vitess?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure I follow. This block was skipping this test when we're running Vitess. We want to run this test for Vitess as well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Got it, thanks

@adrw
Copy link
Collaborator

adrw commented Mar 27, 2025

Is this PR still active / wanting to be merged? Or has it been superceded by another one?

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

Successfully merging this pull request may close these issues.

3 participants