allthingssecurity opened a new pull request, #29440:
URL: https://github.com/apache/camel/pull/29440

   # Description
   
   [CAMEL-26736](https://issues.apache.org/jira/browse/CAMEL-26736)
   
   The same two fixes as CAMEL-26708 (#29405) for `JdbcKeyValueRepository`, 
here for `JpaKeyValueRepository` in camel-jpa.
   
   **putIfAbsent.** When two repositories on one database inserted the same 
key, the `persist` + `flush` of the loser failed with a constraint violation, 
and the key was read again with `findByKey` in the same transaction. The failed 
flush had marked that transaction for rollback, so the loser's `putIfAbsent` 
failed on commit (`DataIntegrityViolationException` with the Spring based 
`DefaultTransactionStrategy`) instead of returning the value of the winner. An 
expired entry was updated in place with `merge`; `KeyValueEntry` has no 
version, so two callers that both read it as expired both updated it and both 
returned `null`: both won.
   
   **Lazy expiry.** `get`, `contains` and `delete` removed an expired entry 
with `entityManager.remove`, which deletes by primary key. An entry that 
another node stored for the key after it was read was deleted with it.
   
   Change:
   - `putIfAbsent` lets the constraint violation roll back the transaction and 
reads the key again in a new transaction, which returns the stored value or 
inserts again; after 3 failed inserts the `PersistenceException` is thrown. A 
constraint violation is also recognised by its SQL state (class `23`), as the 
PostgreSQL and SQL Server drivers report a duplicate key with a plain 
`SQLException` that the JPA provider wraps. An expired entry is deleted (only 
while it is still expired) and the key inserted, so the primary key decides 
between callers for an expired entry as well.
   - An expired entry is deleted with a JPQL `delete ... where e.itemKey = ?1 
and e.expiresAt > 0 and e.expiresAt <= ?2`, so an entry stored again meanwhile 
is kept.
   - No reference to Spring classes is added to `JpaKeyValueRepository` 
(CAMEL-26106).
   
   In a transaction of the caller, the failed insert marks that transaction for 
rollback (as on main), so the caller's transaction fails. Unlike #29405, which 
throws at once when it joined a transaction of the caller, `putIfAbsent` cannot 
tell that here: `TransactionStrategy` exposes no transaction status, and this 
class must not reference Spring classes (CAMEL-26106). So attempts 2 and 3 run 
in that transaction and may return the winner's value or fail; the caller's 
commit fails either way, as on main.
   
   Docs: the `JpaKeyValueRepository` row and note of `keyValueRepository.adoc` 
(and its catalog copy), and an entry in the 4.24 upgrade guide. #29405 rewords 
the same note, so whichever of the two is merged second needs a small rebase of 
that paragraph.
   
   Tests: `JpaKeyValueRepositoryConcurrencyTest` runs two repositories on one 
embedded H2 database (OpenJPA, as `JpaKeyValueRepositoryTest`). The entity 
manager operations of node A go through a proxy that can pause them, so the 
calls of both nodes interleave in a given order (no sleeps):
   - the loser of an insert race gets the value of the winner, also when the 
driver reports the duplicate key only by its SQL state;
   - if the winner's entry is deleted before the loser reads the key again, the 
loser stores its value;
   - of two callers that read the same expired entry, one wins and the other 
gets its value;
   - reading an expired entry keeps the entry another node stored meanwhile;
   - `putIfAbsent` gives up after 3 constraint violations.
   
   On main all 6 fail (`DataIntegrityViolationException` for the insert races, 
`null` / both winning for the expiry tests). Mutants: `remove` back in `get` → 
only the read-expiry test fails; 1 attempt instead of 3 → the five tests that 
need a second attempt fail; no SQL state check → only the SQL state test fails. 
With the fix 6/6, and 600/600 over 20 runs together with 
`JpaKeyValueRepositoryTest`. camel-jpa module tests: 139 run, 0 failures, 1 
skipped (offline).
   
   # Target
   
   - [x] I checked that the commit is targeting the correct branch (Camel 4 
uses the `main` branch)
   
   # Tracking
   - [x] If this is a large change, bug fix, or code improvement, I checked 
there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL) filed for 
the change (usually before you start working on it): 
[CAMEL-26736](https://issues.apache.org/jira/browse/CAMEL-26736).
   
   # Apache Camel coding standards and style
   
   - [x] I checked that each commit in the pull request has a meaningful 
subject line and body.
   - [ ] I have run `mvn clean install -DskipTests` locally from root folder 
and I have committed all auto-generated changes.
     (I built the core modules and camel-jpa with the formatter and import-sort 
plugins and ran the module tests; no generated file changes, and the catalog 
copy of `keyValueRepository.adoc` is identical. I did not run the full root 
build.)
   
   # AI-assisted contributions
   
   - [x] If this PR includes AI-generated code, commits have proper 
co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR 
description identifies the AI tool used.
     This PR was prepared with Claude Code (Claude Opus 5.5). The commit 
carries a `Co-Authored-By` trailer.
   
   _Claude Code on behalf of allthingssecurity_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to