Avoid MySQL and MariaDB chat memory deadlocks during concurrent saves#6649
Open
seeun0210 wants to merge 1 commit into
Open
Avoid MySQL and MariaDB chat memory deadlocks during concurrent saves#6649seeun0210 wants to merge 1 commit into
seeun0210 wants to merge 1 commit into
Conversation
Signed-off-by: seeun0210 <cocobell3@naver.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6655
Thank you for taking the time to contribute to Spring AI!
Problem
JdbcChatMemoryRepository.saveAll()deletes the existing conversation rows and then inserts the replacement messages in one transaction. With MySQL or MariaDB's defaultREPEATABLE_READisolation, concurrent saves for different, previously unseen conversation IDs can acquire overlapping gap locks during the deletes and deadlock when the inserts begin.The new MySQL integration test deterministically reproduces this by pausing four concurrent saves after their deletes. It fails on
mainwithDeadlock found when trying to get lock.Changes
READ_COMMITTEDfor the repository-createdDataSourceTransactionManagerwhen the resolved dialect is MySQL or MariaDB. This prevents the missing-row deletes from taking the conflicting gap locks.ISOLATION_DEFAULTwhen an application supplies its ownPlatformTransactionManager, preserving outer-transaction participation and compatibility with transaction managers that do not support JDBC isolation levels.Design rationale
When only a
DataSourceis supplied, the repository creates its own transaction manager and therefore owns the transaction boundary. Selecting a safe isolation level for its delete-then-insert algorithm avoids requiring every MySQL and MariaDB user to reconfigure an application-wide connection pool. The override applies only to repository-created transactions; it does not change theDataSourcedefault or unrelated application transactions.READ_COMMITTEDpreserves the atomicity of the delete and inserts, whilesaveAll()does not require a repeatable-read snapshot.Trade-off
When an application supplies its own
PlatformTransactionManager, the repository does not override that manager's isolation level. If it starts or joins a MySQL or MariaDBREPEATABLE_READtransaction, concurrent saves can still encounter the same gap-lock deadlock. Applications using an explicit transaction manager remain responsible for selectingREAD_COMMITTEDor handling deadlock retries; forcing an isolation level here would break outer-transaction semantics and can be unsupported by non-JDBC transaction managers.Verification
./mvnw clean package./mvnw -pl memory-repositories/spring-ai-model-chat-memory-repository-jdbc -Pintegration-tests -Dit.test=JdbcChatMemoryRepositoryMysqlIT verify./mvnw -pl memory-repositories/spring-ai-model-chat-memory-repository-jdbc -Dtest=JdbcChatMemoryRepositoryBuilderTests test./mvnw -pl memory-repositories/spring-ai-model-chat-memory-repository-jdbc process-sourcesContributor checklist
Signed-off-byline (git commit -s) per the DCO.main.