shashank created CAMEL-26743:
--------------------------------

             Summary: camel-sql - JdbcOrphanLockAwareIdempotentRepository: the 
keep-alive deadlocks with add and remove on a small connection pool, and leases 
expire
                 Key: CAMEL-26743
                 URL: https://issues.apache.org/jira/browse/CAMEL-26743
             Project: Camel
          Issue Type: Bug
          Components: camel-sql
            Reporter: shashank
            Assignee: shashank


{{JdbcOrphanLockAwareIdempotentRepository}} guards its set of held keys with a 
{{StampedLock}}, and holds the lock while SQL runs:

* {{keepAlive()}} takes the read lock and then starts its transaction, which 
borrows a connection from the pool.
* {{add()}} and {{remove()}} borrow a connection first (the transaction of 
{{AbstractJdbcMessageIdRepository}}), and then take the write lock in 
{{insert}} / {{delete}}, around the COUNT, INSERT, UPDATE or DELETE.

With more threads using the repository than the pool has connections, the two 
orders form a cycle: the keep-alive holds the read lock and waits for a 
connection, while every connection is held by an add or remove that waits for 
the write lock. With a pool that waits as long as it takes (the defaults of 
plain Agroal and DBCP2) the node deadlocks: no add or remove completes again. 
With a pool timeout (HikariCP: 30 s) the keep-alive round fails after the 
timeout, only logged at ERROR, and the add/remove calls stall until then; under 
steady load the next round can be caught the same way. With an infinite wait no 
lease is refreshed again; with a timeout, when rounds keep failing for 
{{lockMaxAgeMillis}}, the leases expire. Then another node takes over keys this 
node is still processing, so a message is processed twice at the same time.

The same lock also makes the keep-alive wait for any slow statement of an add 
or remove: an INSERT that waits for a row lock of another node (for example a 
transacted route on that node holding an uncommitted insert of the same key) 
blocks the keep-alive of the whole node for as long as it waits. PostgreSQL, 
Oracle and SQL Server have no lock timeout by default.

The lock only protects the in-memory set; it does not make the COUNT and the 
INSERT atomic, as the transaction of add or remove commits after the lock is 
released.

Expected: the keep-alive and add/remove never wait for each other while one of 
them holds a connection, and the keep-alive refreshes the held keys while an 
add or remove runs.

Suggested fix: keep the held keys in a concurrent set and hold no lock across 
SQL; the keep-alive takes its timestamp and the keys once it has its connection.

_Filed with Claude Code on behalf of allthingssecurity._



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to