Conversation
083987b to
36fce97
Compare
36fce97 to
874faff
Compare
| */ | ||
| @Test | ||
| @Order(50) | ||
| fun `SEP-6 deposit with pending-external status`() { |
There was a problem hiding this comment.
This is no longer a valid test after implement optimistic lock since step 2 and step 3 are essentially competing with each other. The realistic way is
pending_user_transfer_start -> notify_offchain_funds_sent
pending external -> notify_offchain_funds_received
Which has already been covered by other test cases
There was a problem hiding this comment.
Pull request overview
Adds optimistic locking and retry/error-handling to prevent TOCTOU races during transaction updates, ensuring concurrent modifications are detected and surfaced as retryable failures instead of silently overwriting state.
Changes:
- Add
@VersiontoJdbcSepTransactionand a Flyway migration (V25) to add aversioncolumn to SEP 6/24/31 transaction tables. - Add optimistic-lock retry loops and retryable error messaging in
RpcServiceandTransactionService. - Add/adjust tests for optimistic locking handling and tweak reference server event processing to wait for transaction visibility.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| platform/src/main/java/org/stellar/anchor/platform/data/JdbcSepTransaction.java | Adds JPA @Version field to enable optimistic locking. |
| platform/src/main/resources/db/migration/V25__add_optimistic_lock_version.sql | Adds version column to transaction tables. |
| platform/src/main/java/org/stellar/anchor/platform/service/TransactionService.java | Retries PATCH transaction updates on optimistic-lock conflicts and maps failures to retryable errors. |
| platform/src/main/java/org/stellar/anchor/platform/service/RpcService.java | Retries RPC processing on optimistic-lock conflicts; clears persistence context between retries. |
| platform/src/test/kotlin/org/stellar/anchor/platform/service/TransactionServiceTest.kt | Adds unit test coverage for optimistic-lock failure mapping to BadRequestException. |
| platform/src/test/kotlin/org/stellar/anchor/platform/service/RpcServiceTest.kt | Adds unit test coverage for optimistic-lock failure mapping to JSON-RPC internal error with retry message. |
| kotlin-reference-server/src/main/kotlin/org/stellar/reference/event/processor/Sep31EventProcessor.kt | Adds retry/wait to reduce flakiness when events arrive before transaction is readable via API. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| // Retries on OptimisticLockingFailureException, which occurs when two concurrent requests | ||
| // modify the same transaction. All other exceptions propagate immediately. | ||
| private Object processRpcCallWithRetry(RpcRequest rpcCall) throws AnchorException { |
There was a problem hiding this comment.
I'd return a conflict error to the client instead of silently retrying. A optimistic lock failure means that the state has changed since the RPC call was made. That's not really a transient issue that should be retried automatically since the call can now be invalid. The client should reread the state if they observe this error and decide whether to retry.
…#1905) Defer event publishing to after database transaction commit using a TransactionSynchronization decorator, preventing race conditions where consumers receive events before transaction data is visible. Remove the retry/polling workaround in the reference server's Sep31EventProcessor that was compensating for this timing issue.
Description
@Versionoptimistic locking toJdbcSepTransactionto prevent concurrent modifications from silently overwriting each otherversioncolumn on all transaction tablesOptimisticLockingFailureExceptioninRpcServiceandTransactionServicewith clear retry error messages, and retry logic inTransactionServiceandrpcServicetests
Sep31PlatformApiTestsdue to race between event delivery and DB writeSEP-6 deposit with pending-external status(see comments)Context
A bounty report identified a TOCTOU race condition: the RPC handler reads a transaction, validates in memory, then saves — with no concurrency control. Two concurrent requests can both pass validation against stale state, causing silent data corruption (e.g., double-refund). Optimistic locking makes the second concurrent save fail explicitly instead.
Testing
RpcServiceTest— verifiesOptimisticLockingFailureExceptionreturns JSON-RPC error code -32603 with retry messageTransactionServiceTest— verifiesOptimisticLockingFailureExceptionon save is caught and re-thrown asBadRequestException