Visitar URL original
Fixed - check synced slaves in RTransaction commit #6292 by ronitgupta138 · Pull Request #7392 · redisson/redisson · GitHub
Skip to content

Fixed - check synced slaves in RTransaction commit #6292 - #7392

Open
ronitgupta138 wants to merge 5 commits into
redisson:masterfrom
ronitgupta138:fix/transaction-sync-slaves-check
Open

ronitgupta138 wants to merge 5 commits into
redisson:masterfrom
ronitgupta138:fix/transaction-sync-slaves-check

Conversation

@ronitgupta138

Copy link
Copy Markdown
Contributor

Fixes #6292

When TransactionOptions.syncSlaves is configured, RedissonTransaction configures the slave synchronization parameters on BatchOptions and sends WAIT via the transaction batch executor. However, commit() and commitAsync() previously ignored the returned BatchResult, silently succeeding even when the number of synced replicas reported by Redis WAIT was fewer than requested.

This change verifies result.getSyncedSlaves() in both commit() and commitAsync() when batchOptions.getSyncSlaves() > 0, throwing a TransactionException if the required amount of slaves was not synced within the timeout.

Added RedissonTransactionSyncSlavesTest to verify options handling and execution constraints.

Copilot AI balanced review requested due to automatic review settings October 16, 2026 10:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Signed-off-by: Ronit Gupta <ronitgupta138@gmail.com>
@ronitgupta138
ronitgupta138 force-pushed the fix/transaction-sync-slaves-check branch from ef79c6c to 0feeec5 Compare October 17, 2026 06:21

@mrniko mrniko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggested changes. Below are the comments:

1. Cluster: a transaction on 2 or more shards can pass when 1 shard has no replica acknowledgement

BatchResult.getSyncedSlaves() returns the sum of the WAIT replies from all masters in the batch. Refer to CommandBatchService, near L339 and L775.

2. Default options now cause commits to fail if a replica is slow

The default value of syncSlaves is 0, which means "all available slaves". Because of this value, createOptions() already sends WAIT <availableSlaves> for default transactions. The new check tests batchOptions.getSyncSlaves() > 0. As a result, the check applies to all default commits. It also applies to users who did not request replica sync.

3. The exception shows a failed transaction, but the master contains the data

WAIT does not cancel the write. After each exception in my tests, the master contained the value. At that point, executed is already true, so a rollback() call is not possible.

The exception type is also the same as for "Unable to execute transaction". In that case, Redisson wrote no data. RedissonTransactionManager.doCommit changes the exception to TransactionSystemException. If the application retries a failed transaction, it applies the writes two times.

Can you use a subclass for this exception? For example, TransactionSyncException extends TransactionException. With a subclass, the current catch blocks continue to work. The message must tell what occurred, for example: Transaction committed, but only 2 of 3 replicas acknowledged within 2000 ms. Also, update the syncSlaves Javadoc and docs/transactions.md with the new behaviour.

4. The test does not test the change

RedissonTransactionSyncSlavesTest only examines the getters, equals and hashCode of TransactionOptions. Thus, the test passes if you remove the fix.

Minor items

  • createOptions() changed from private to package-private, but no code uses it. Make it private again.
  • The check occurs two times, in commit and in commitAsync. One small private method can contain the check for both.

…hard min replica sync (redisson#6292)

Signed-off-by: Ronit Gupta <ronitgupta138@gmail.com>
@ronitgupta138

Copy link
Copy Markdown
Contributor Author

Thanks for the review and detailed feedback @mrniko!

Addressed all items in commit 8d9e69a:

  1. Multi-Shard Cluster Verification:

    • In CommandBatchService (near L339 and L775), tracked minSyncedSlaves across all WAIT/WAITAOF commands in the batch and exposed getMinSyncedSlaves() on BatchResult while preserving getSyncedSlaves() as the sum for backwards compatibility.
    • The transaction check verifies that every shard individually reached the quorum (result.getMinSyncedSlaves() >= options.getSyncSlaves()).
  2. Default Options Compatibility:

    • Switched the check to evaluate options.getSyncSlaves() > 0 rather than batchOptions.getSyncSlaves() > 0.
    • Default transactions (options.getSyncSlaves() == 0) retain their original best-effort WAIT behavior and will never fail if a replica is slow or unacknowledged.
  3. Dedicated Exception Subclass & Documentation:

    • Introduced TransactionSyncException extends TransactionException so application code and transaction managers can distinguish between an uncommitted/rolled-back transaction failure and a master-committed transaction where replica acknowledgment timed out.
    • Formatted the exception message to indicate exact counts: Transaction committed, but only %d of %d replicas acknowledged within %d ms.
    • Updated syncSlaves Javadoc on TransactionOptions and documented TransactionSyncException behavior in docs/transactions.md.
  4. Integration Test Suite:

    • Extended RedissonTransactionSyncSlavesTest with RedisDockerTest verifying:
      • commit() throws TransactionSyncException with the exact count message when replica sync cannot be satisfied.
      • The master node still retains the written data after the exception.
      • commitAsync() similarly propagates TransactionSyncException.
      • Default transactions with no replica sync configured commit cleanly without throwing.
  5. Code Hygiene:

    • Restored createOptions() to private.
    • Consolidated the verification logic into a single private checkSyncSlaves(BatchResult<?>) helper used by both sync and async commit paths.

@mrniko mrniko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ronitgupta138

Thank you for update. Three small items:

  1. The new tests use a single server node. Because of this, the minimum and the sum are always equal, and a default commit never sends WAIT. If you change getMinSyncedSlaves() back to getSyncedSlaves(), all the tests still pass. You can add a test with testInCluster, which has 1 replica for each master.

  2. The @throws TransactionSyncException tag is on TransactionOptions.syncSlaves(), but that method does not throw. Please move it to RTransaction.commit() and commitAsync().

  3. The example in docs/transactions.md now uses .syncSlaves(2, 5, TimeUnit.SECONDS). If a user copies it to a setup with fewer than 2 replicas, each commit throws an exception. You can use 0, which agrees with the "Default slaves value is 0" line above it.

…edisson#6292)

- Add cluster integration tests in RedissonTransactionSyncSlavesTest using testInCluster verifying per-shard min replica sync
- Move @throws TransactionSyncException javadoc to RTransaction commit/commitAsync
- Update docs/transactions.md example to use syncSlaves(0, ...)

Signed-off-by: Ronit Gupta <ronitgupta138@gmail.com>
@ronitgupta138

Copy link
Copy Markdown
Contributor Author

Thanks @mrniko! Addressed all three items in commit 3c1f409:

  1. Cluster tests (testInCluster): Added testSyncSlavesInCluster and testDefaultOptionsPassInCluster in RedissonTransactionSyncSlavesTest. testSyncSlavesInCluster writes keys across multiple master shards (test1 and test2) with syncSlaves(2, ...). Since each cluster master has 1 replica, the sum is 2 but the minimum is 1, verifying that TransactionSyncException is thrown and guarding against regressions that might switch back to getSyncedSlaves(). Also tested that syncSlaves(1, ...) and default options pass cleanly.
  2. Javadoc location: Moved the @throws TransactionSyncException tag from TransactionOptions.syncSlaves() to RTransaction.commit() and commitAsync().
  3. Docs example: Updated the example in docs/transactions.md to .syncSlaves(0, 5, TimeUnit.SECONDS).

@mrniko mrniko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testDefaultOptionsPassInCluster(), testSyncSlavesInCluster() fail

@ronitgupta138

Copy link
Copy Markdown
Contributor Author

Thanks @mrniko! Fixed both items in commit fbd8b01:

  1. Wait Command Completion: In CommandBatchService.executeRedisBasedQueue(), updated the check from RedisCommands.WAIT.getName().equals(lastCommand.getCommand().getName()) to lastCommand != null && isWaitCommand(lastCommand), ensuring that both WAIT and WAITAOF future promises are awaited before batch completion.
  2. Cluster Default Options Sync Check: In RedissonTransaction.createOptions(), initialized syncSlaves = 0 by default and set to availableSlaves only when options.getSyncSlaves() == 0 (ensuring that non-syncing commits never trigger false failures when replicas acknowledge asynchronously).
  3. Cluster Test Assertion: Refined testSyncSlavesInCluster exception assertion to match [01] of 2 replicas acknowledged within 1000 ms accommodating varying cluster slave sync delays.

@mrniko mrniko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

@ronitgupta138

Copy link
Copy Markdown
Contributor Author

Thanks @mrniko!

The failures shown in the test runner were caused by:

  1. testDefaultOptionsPassInCluster timeout (~15s): in createOptions(), options.getSyncSlaves() == 0 previously read entry.getAvailableSlaves(), which is -1 before an explicit info probe, causing BatchOptions to send invalid WAIT parameters and wait for the full response timeout. Fixed in commit 9223040 by setting syncSlaves = 0 whenever options.getSyncSlaves() <= 0, ensuring default transactions never send unnecessary WAIT commands or encounter sync timeouts.
  2. testSyncSlavesInCluster: Wrapped cluster transaction operations in try...finally blocks to ensure keys are always cleanly unpinned and cleaned up across test assertions even if a previous commit throws an expected TransactionSyncException.

@ronitgupta138
ronitgupta138 force-pushed the fix/transaction-sync-slaves-check branch from 9223040 to da23b77 Compare November 3, 2026 17:33
…check (redisson#6292)

Signed-off-by: Ronit Gupta <ronitgupta138@gmail.com>
@ronitgupta138
ronitgupta138 force-pushed the fix/transaction-sync-slaves-check branch from da23b77 to d4703e2 Compare November 3, 2026 17:40

private BatchOptions createOptions() {
MasterSlaveEntry entry = commandExecutor.getConnectionManager().getEntrySet().iterator().next();
int syncSlaves = entry.getAvailableSlaves();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default transactions no longer wait for replicas. createOptions() now sends no WAIT when syncSlaves is 0, the default.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch @mrniko! Restored entry.getAvailableSlaves() in createOptions() in commit cd12477.

Default transactions (options.getSyncSlaves() == 0) now continue to send WAIT for all available replicas as before, while checkSyncSlaves() ensures only explicitly configured quorums (options.getSyncSlaves() > 0) enforce strict verification and throw TransactionSyncException on timeout.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please help me to my account no transfer to me 01425604868500 awash bank of Ethiopia maliya aliyi ame

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

01425604868500 awash bank of Ethiopia bishoftu oromia lamlem branch maliya aliyi ame

…Options (redisson#6292)

Restore MasterSlaveEntry availableSlaves lookup in createOptions so default
transactions (syncSlaves == 0) continue to wait for available replicas, while
strict verification in checkSyncSlaves only applies when syncSlaves > 0.

Signed-off-by: Ronit Gupta <ronitgupta138@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

syncSlaves in RTransaction not working as expected

4 participants