Skip to content

fix(repos): catch Spring DataAccessException so CrdbRetry and PROCTRAN abends fire#37

Merged
a2chang merged 3 commits intomainfrom
polish/dae-translator-fix
May 1, 2026
Merged

fix(repos): catch Spring DataAccessException so CrdbRetry and PROCTRAN abends fire#37
a2chang merged 3 commits intomainfrom
polish/dae-translator-fix

Conversation

@a2chang
Copy link
Copy Markdown
Contributor

@a2chang a2chang commented May 1, 2026

Problem

Per #36: Spring Boot 3's JooqAutoConfiguration registers
DefaultExceptionTranslatorExecuteListener, which intercepts every
SQLException raised by jOOQ and substitutes an
org.springframework.dao.DataAccessException via ctx.exception(...). The
Spring DAE is not a subtype of org.jooq.exception.DataAccessException,
so every catch (org.jooq.exception.DataAccessException) block in this
codebase missed at runtime. Concretely:

The global @ControllerAdvice still classified the uncaught Spring DAE
as UNEX, which is why no test alarm went off until now: the only way to
observe the gap is to make a write actually fail at the database layer,
which the existing test suite did not exercise.

Fix

  • Swap the import to org.springframework.dao.DataAccessException across
    all 13 main files. The catch sites use the simple class name only, so
    the bodies are unchanged. isSerializationFailure(...) already walks
    the cause chain looking for SQLException with SQLSTATE = 40001, which
    works the same way against either DAE flavour. CbsaAbendException
    accepts any Throwable as cause, so no further adjustments are needed.
  • Test mocks already used new DataAccessException(...) { } anonymous
    subclasses (Spring DAE is abstract; jOOQ DAE is concrete but the
    anonymous form works for both), so unit tests need only the import
    swap as well.

Regression coverage

Add the integration tests deferred from #34:

  • CrecustServiceIntegrationTest#proctranInsertFailureSurfacesHwptAbend
  • UpdcustServiceIntegrationTest#proctranInsertFailureSurfacesHwptAbend

Both force every PROCTRAN insert to fail via
ALTER TABLE proctran ADD CONSTRAINT ... CHECK (false) NOT VALID and
assert that CbsaAbendException("HWPT") surfaces, exercising the inner
catches that unit tests cannot reach (the lambda runs against
DSL.using(configuration) inside transactionResult).

Pre-fix, both tests fail with
org.springframework.dao.DataIntegrityViolationException bubbling out
of the service. Post-fix, both pass.

Docs

Add a bullet to docs/translation-rules.md §12 spelling out which DAE to
import and why, so future code generation does not regress.

Verification

  • ./mvnw -B verify187 tests pass, including the two new
    integration tests.
  • All 13 catch sites are now reachable at runtime by virtue of catching
    the Spring DAE that the translator actually emits.

Closes #36.

…N abends fire

Spring Boot 3's JooqAutoConfiguration registers
DefaultExceptionTranslatorExecuteListener, which intercepts every
SQLException raised by jOOQ and substitutes an
org.springframework.dao.DataAccessException via ctx.exception(...). The
Spring DAE is not a subtype of org.jooq.exception.DataAccessException,
so every catch (org.jooq.exception.DataAccessException) block in this
codebase missed at runtime. Concretely:

- CrdbRetry.run(...) did not retry SQLSTATE 40001 — the Spring DAE
  bypassed the catch and propagated immediately.
- Inner PROCTRAN catches (CrecustRepository, UpdcustRepository,
  CreaccRepository, UpdaccRepository, DbcrfunRepository) did not
  translate failures into HWPT or domain fail codes.
- Outer XRTY catches added in #33 did not fire either.

Swap the import to org.springframework.dao.DataAccessException across
all 13 main files. The catch sites use the simple class name only, so
the body is unchanged. Test mocks already used 'new DataAccessException(...) { }'
anonymous subclasses (Spring DAE is abstract; jOOQ DAE is concrete but
the anonymous form works for both), so unit tests need only the import
swap as well.

Add the regression integration tests deferred from #34:
- CrecustServiceIntegrationTest#proctranInsertFailureSurfacesHwptAbend
- UpdcustServiceIntegrationTest#proctranInsertFailureSurfacesHwptAbend

Both force every PROCTRAN insert to fail via 'CHECK (false) NOT VALID'
and assert the HWPT abend surfaces, exercising the inner catches that
unit tests cannot reach (the lambda runs against DSL.using(configuration)
inside transactionResult).

Document the constraint in translation-rules.md alongside the existing
PROCTRAN audit-trail rule so future code generation imports the right
DAE.

Closes #36
@a2chang
Copy link
Copy Markdown
Contributor Author

a2chang commented May 1, 2026

augment review

Copy link
Copy Markdown

@augment-app-staging augment-app-staging Bot left a comment

Choose a reason for hiding this comment

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

Review completed. 1 suggestion posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

// never matches and the test sees a Spring DataIntegrityViolation
// bubble out instead, classified as UNEX.
when(reviewDateRandom.nextInt(20)).thenReturn(3);
dsl.execute("ALTER TABLE proctran ADD CONSTRAINT proctran_block_inserts CHECK (false) NOT VALID");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Because AbstractCockroachIntegrationTest shares a singleton Cockroach container across test classes, if this ALTER TABLE ... ADD CONSTRAINT throws (e.g. constraint already exists from a prior failure or parallel execution), the test will exit before the finally and leave the schema poisoned for later tests. Consider ensuring the schema cleanup runs even when the constraint-add step fails.

Severity: medium

Other Locations
  • src/test/java/com/augment/cbsa/service/UpdcustServiceIntegrationTest.java:112

🤖 Was this useful? React with 👍 or 👎

Copy link
Copy Markdown

@augment-app-staging augment-app-staging Bot left a comment

Choose a reason for hiding this comment

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

Review completed. 1 suggestion posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

// never matches and the test sees a Spring DataIntegrityViolation
// bubble out instead, classified as UNEX.
when(reviewDateRandom.nextInt(20)).thenReturn(3);
dsl.execute("ALTER TABLE proctran ADD CONSTRAINT proctran_block_inserts CHECK (false) NOT VALID");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Because the ALTER TABLE ... ADD CONSTRAINT runs outside the try/finally, if it fails (e.g., the constraint already exists from a previous failed run), the cleanup won’t execute and later integration tests may be affected by the leftover constraint.

Severity: medium

Other Locations
  • src/test/java/com/augment/cbsa/service/UpdcustServiceIntegrationTest.java:112

🤖 Was this useful? React with 👍 or 👎

Round-1 review noted that the ALTER TABLE ... ADD CONSTRAINT call sat
outside the try/finally, so a failure on the ADD itself (e.g. the
constraint already exists from a previously-killed JVM, since
AbstractCockroachIntegrationTest reuses a singleton container across
test classes) would skip the cleanup and poison every later PROCTRAN
write in the same JVM.

Move the ADD inside the try and use DROP CONSTRAINT IF EXISTS for the
cleanup so the schema returns to a known-good state regardless of where
the test fails.
@a2chang
Copy link
Copy Markdown
Contributor Author

a2chang commented May 1, 2026

Round 1 → Round 2 response

Both review rounds flagged the same medium issue on the integration tests: the ALTER TABLE ... ADD CONSTRAINT proctran_block_inserts CHECK (false) NOT VALID call ran outside the try/finally, so a failure on the ADD itself (e.g. the constraint already exists from a previously-killed JVM — AbstractCockroachIntegrationTest reuses a singleton container across test classes) would skip cleanup and poison every later PROCTRAN write.

Fixed in d38fbc9 by:

  1. Moving the ADD inside the try block so the cleanup finally always fires.
  2. Using DROP CONSTRAINT IF EXISTS for the cleanup so the schema returns to a known-good state regardless of where the test fails (and a stale constraint from a previous run is drained on the way in too).

Local verify still green (7/7 across the two integration test classes).

augment review

Copy link
Copy Markdown

@augment-app-staging augment-app-staging Bot left a comment

Choose a reason for hiding this comment

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

Review completed. 1 suggestion posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

// use DROP ... IF EXISTS so we also recover from any constraint a
// previously-killed JVM may have left behind.
try {
dsl.execute("ALTER TABLE proctran ADD CONSTRAINT proctran_block_inserts CHECK (false) NOT VALID");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If proctran_block_inserts already exists before this test starts (e.g., from a previously-killed JVM in a shared Cockroach container), the ALTER TABLE ... ADD CONSTRAINT will throw before the assertion runs and make the test flaky. The comment mentions recovering from leftover constraints, but that recovery currently only happens in the finally after the add attempt.

Severity: medium

Other Locations
  • src/test/java/com/augment/cbsa/service/UpdcustServiceIntegrationTest.java:119

🤖 Was this useful? React with 👍 or 👎

Round-2 review: even with the cleanup moved into try/finally, a leftover
proctran_block_inserts constraint from a previously-killed JVM would
still make the next test flaky because the recovery only ran in
finally, after the ADD. Drop pre-emptively before adding so the test is
idempotent on both ends.
@a2chang
Copy link
Copy Markdown
Contributor Author

a2chang commented May 1, 2026

Round 2 → Round 3 response

Round 2 narrowed the medium issue to the on-the-way-in case: a leftover constraint from a previously-killed JVM would still make the next test flaky because the recovery only ran in finally, after a successful ADD.

Fixed by adding a pre-emptive ALTER TABLE proctran DROP CONSTRAINT IF EXISTS proctran_block_inserts immediately before the try. Combined with the existing IF EXISTS cleanup in finally, the test is now safe regardless of:

  1. Whether a stale constraint already exists when the test starts (drained on the way in).
  2. Whether the assertion or service call throws mid-test (cleanup in finally still fires).
  3. Whether the test is re-run within the same shared container after a previous failure (both ends idempotent).

Local verify still green (7/7 across the two integration test classes).

augment review

Copy link
Copy Markdown

@augment-app-staging augment-app-staging Bot left a comment

Choose a reason for hiding this comment

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

Review completed. No suggestions at this time.

Comment augment review to trigger a new review at any time.

@a2chang a2chang merged commit 52edc33 into main May 1, 2026
1 check passed
@a2chang a2chang deleted the polish/dae-translator-fix branch May 1, 2026 20:10
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.

polish: jOOQ DataAccessException catches don't fire because Spring Boot translator substitutes Spring DAE

1 participant