Files
james.maesandClaude Sonnet 5.5 024cfc21a9
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (pull_request) Successful in 12s
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 9s
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 6s
maven / build (push) Successful in 11m4s
build / maven (push) Successful in 11m4s
release: prepare Collaboration 1.0 (Core RC.22 BOM, Redis RC.1, current status docs)
Replace the default-active Core snapshot profile with the released Core
4.1.0-RC.22 BOM, pin qbit-redis to its first release candidate, and replace
the snapshot-era status text with the current 1.0 status.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 17:25:50 -06:00

35 lines
2.6 KiB
Markdown

# Core dependency: after-rollback callbacks
Collaboration's retained-upload and cleanup paths compensate external object storage only after a transaction's rollback is confirmed. They need two methods on `com.kingsrook.qqq.backend.core.actions.QBackendTransaction`. These are included in QQQ Core `4.1.0-RC.22`, which this module imports through the BOM; Core `4.1.0-RC.21` does not declare them.
## Required API (reviewed implementation: `b6fb2382`, `third-party-src/qqq-core`)
`qqq-backend-core`, class `QBackendTransaction`:
```java
public boolean supportsRollbackCallbacks() // default: false
public void addAfterRollbackCallback(Runnable callback) // throws UnsupportedOperationException unless supportsRollbackCallbacks(); Objects.requireNonNull(callback)
protected void runAfterRollbackCallbacks() // run once, in order, then clear; exceptions (incl. LinkageError) logged and swallowed
```
Pending rollback callbacks are preserved when a required operation marks the transaction rollback-only, and run only after confirmed JDBC rollback. A failed physical commit clears them because its outcome is uncertain. Successful commit discards them.
`qqq-backend-module-rdbms`, class `RDBMSTransaction`:
```java
@Override public boolean supportsRollbackCallbacks() // true
@Override public void addAfterRollbackCallback(Runnable callback) // super.add...; then rolledBackSinceCommit = false
// rollback()/close(): after connection.rollback() succeeds -> rolledBackSinceCommit = true; runAfterRollbackCallbacks()
// close() rolls back (and so runs callbacks) unless a rollback already happened since the last commit / callback registration
```
## Callers in this module
- `attachments/AttachmentsService.storeUpload`: refuses an ambient transaction where `!supportsRollbackCallbacks()`; registers storage compensation.
- `attachments/AttachmentCleanupQueue.beginUpload`: registers the same cleanup on both `addAfterRollbackCallback` and `addAfterCommitCallback`.
## Core tests to carry with the change
- Base class: `supportsRollbackCallbacks()` is false; `addAfterRollbackCallback` throws `UnsupportedOperationException`.
- RDBMS: callback runs after `rollback()`; does not run after `commit()`; runs when a transaction with a registered callback is closed without commit;
a callback registered after an earlier rollback still runs on close; a throwing callback does not stop later callbacks.
- Integration (already in this module, needs the core): `attachments/AttachmentTransactionsPostgresTest` (needs Docker) incl. `interruptedUploadSurvivesLostRollbackCallback`.