Skip to content

Conversation

@brfrn169
Copy link
Collaborator

@brfrn169 brfrn169 commented Jun 20, 2025

Description

This PR adds an optimization for handling mutations in Consensus Commit based on the storage’s mutation atomicity unit.

Currently, Consensus Commit always splits the necessary mutations by partition and executes them using the DistributedStorage.mutate() method. However, we can instead split the mutations based on the storage’s mutation atomicity unit and execute them accordingly. That’s the basic idea behind this optimization.

Related issues and/or PRs

N/A

Changes made

Added some inline comments. Please take a look for the details.

Checklist

The following is a best-effort checklist. If any items in this checklist are not applicable to this PR or are dependent on other, unmerged PRs, please still mark the checkboxes after you have read and understood each item.

  • I have commented my code, particularly in hard-to-understand areas.
  • I have updated the documentation to reflect the changes.
  • I have considered whether similar issues could occur in other products, components, or modules if this PR is for bug fixes.
  • Any remaining open issues linked to this PR are documented and up-to-date (Jira, GitHub, etc.).
  • Tests (unit, integration, etc.) have been added for the changes.
  • My changes generate no new warnings.
  • Any dependent changes in other PRs have been merged and published.

Additional notes (optional)

N/A

Release notes

Added an optimization to Consensus Commit that splits mutations based on the storage’s mutation atomicity unit, improving efficiency and performance in certain storage implementations.

@brfrn169 brfrn169 requested a review from Copilot June 20, 2025 06:30
@brfrn169 brfrn169 self-assigned this Jun 20, 2025
@brfrn169 brfrn169 changed the title Optimize mutations in Consensus Commit based on the storage’s mutation atomicity unit Optimize mutations based on the storage’s mutation atomicity unit in Consensus Commit Jun 20, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR replaces the existing PartitionedMutations approach with a new MutationsGrouper that batches storage mutations according to each storage’s mutation atomicity unit, and updates all call sites accordingly.

  • Introduce MutationsGrouper and wire it into CommitHandler, CommitHandlerWithGroupCommit, ConsensusCommitManager, and TwoPhaseConsensusCommitManager
  • Remove the old PartitionedMutations class and its tests
  • Update integration tests to verify the number of storage.mutate(...) calls based on the configured atomicity unit

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
integration-test/.../ConsensusCommitSpecificIntegrationTestBase.java Switch on StorageInfo.getMutationAtomicityUnit() in two existing tests
integration-test/.../ConsensusCommitNullMetadataIntegrationTestBase.java Instantiate and pass MutationsGrouper in createCommitHandler
core/.../PartitionedMutationsTest.java Deleted obsolete tests for the removed PartitionedMutations class
core/.../PartitionedMutationsKeyTest.java Deleted obsolete tests for the removed PartitionedMutations.Key
core/.../MutationsGrouperTest.java Added comprehensive tests for new MutationsGrouper
core/.../CommitHandlerWithGroupCommitTest.java Updated test helper to include MutationsGrouper
core/.../CommitHandlerTest.java Inject and mock StorageInfoProvider and MutationsGrouper
core/.../TwoPhaseConsensusCommitManager.java Pass MutationsGrouper into 2PC manager constructor
core/.../PartitionedMutations.java Removed retired class
core/.../MutationsGrouper.java New class that groups and batches mutations based on atomicity unit
core/.../ConsensusCommitManager.java Wire in MutationsGrouper in commit handler factory
core/.../CommitHandlerWithGroupCommit.java Add MutationsGrouper field and constructor parameter
core/.../CommitHandler.java Replace PartitionedMutations with MutationsGrouper.groupMutations
Comments suppressed due to low confidence (2)

integration-test/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitSpecificIntegrationTestBase.java:7189

  • [nitpick] This test method name is extremely long and reduces readability. Consider shortening it to clearly state the scenario and expected behavior—e.g., putAndCommit_varyingAtomicity_shouldBatchMutationsCorrectly().
      putAndCommit_SinglePartitionMutationsGiven_ShouldBehaveCorrectlyBasedOnStorageMutationAtomicityUnit(

core/src/test/java/com/scalar/db/transaction/consensuscommit/CommitHandlerWithGroupCommitTest.java:70

  • The variable storageInfoProvider is not declared in this test class. Add @Mock protected StorageInfoProvider storageInfoProvider; and initialize mocks (e.g., via MockitoAnnotations.openMocks(this);) before using it.
        new MutationsGrouper(storageInfoProvider),

Comment on lines 89 to +91
@BeforeEach
void setUp() throws Exception {
MockitoAnnotations.openMocks(this).close();
Copy link

Copilot AI Jun 20, 2025

Choose a reason for hiding this comment

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

Calling .close() immediately after openMocks tears down the mocks before the test runs. Instead, store the returned AutoCloseable in a field and close it in an @AfterEach method, or omit .close() here.

Suggested change
@BeforeEach
void setUp() throws Exception {
MockitoAnnotations.openMocks(this).close();
private AutoCloseable autoCloseableMocks;
@BeforeEach
void setUp() throws Exception {
autoCloseableMocks = MockitoAnnotations.openMocks(this);

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +35

@BeforeEach
public void setUp() throws Exception {
MockitoAnnotations.openMocks(this).close();

grouper = new MutationsGrouper(storageInfoProvider);
}

Copy link

Copilot AI Jun 20, 2025

Choose a reason for hiding this comment

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

Similar to other tests, closing the Mockito session immediately will deactivate mocks. Keep the AutoCloseable returned by openMocks in a field and close in an @AfterEach.

Suggested change
@BeforeEach
public void setUp() throws Exception {
MockitoAnnotations.openMocks(this).close();
grouper = new MutationsGrouper(storageInfoProvider);
}
private AutoCloseable mocks;
@BeforeEach
public void setUp() throws Exception {
mocks = MockitoAnnotations.openMocks(this);
grouper = new MutationsGrouper(storageInfoProvider);
}
@AfterEach
public void tearDown() throws Exception {
if (mocks != null) {
mocks.close();
}
}

Copilot uses AI. Check for mistakes.
import javax.annotation.concurrent.ThreadSafe;

@ThreadSafe
public class MutationsGrouper {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added MutationsGrouper that groups the mutations by based on the storage’s mutation atomicity unit.

new PrepareMutationComposer(snapshot.getId(), tableMetadataManager);
snapshot.to(composer);
PartitionedMutations mutations = new PartitionedMutations(composer.get());
List<List<Mutation>> groupedMutations = mutationsGrouper.groupMutations(composer.get());
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Split the mutations for prepare-records.

new CommitMutationComposer(snapshot.getId(), tableMetadataManager);
snapshot.to(composer);
PartitionedMutations mutations = new PartitionedMutations(composer.get());
List<List<Mutation>> groupedMutations = mutationsGrouper.groupMutations(composer.get());
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Split the mutations for commit-records.

new RollbackMutationComposer(snapshot.getId(), storage, tableMetadataManager);
snapshot.to(composer);
PartitionedMutations mutations = new PartitionedMutations(composer.get());
List<List<Mutation>> groupedMutations = mutationsGrouper.groupMutations(composer.get());
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Split the mutations for rollback-records.

Copy link
Contributor

@Torch3333 Torch3333 left a comment

Choose a reason for hiding this comment

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

LGTM, thank you!

Copy link
Contributor

@feeblefakie feeblefakie left a comment

Choose a reason for hiding this comment

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

LGTM! Thank you!

public List<List<Mutation>> groupMutations(Collection<Mutation> mutations)
throws ExecutionException {
// MutationGroup mutations by their storage info and atomicity unit
List<MutationGroup> groups = new ArrayList<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

[minor] This can be removed by using groupToBatches.keySet() instead?

assert mutation.forNamespace().isPresent();
StorageInfo storageInfo = storageInfoProvider.getStorageInfo(mutation.forNamespace().get());

MutationGroup matchedGroup = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

[minor] If MutationGroup.equals() has a similar logic to isSameGroup(), maybe these lines can be simplified like this?

      MutationGroup group = new MutationGroup(mutation, storageInfo);

      List<List<Mutation>> batches =
          groupToBatches.computeIfAbsent(group, g -> new ArrayList<>());
      int maxCount = group.storageInfo.getMaxAtomicMutationsCount();

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You are right. Fixed in b44e397. Thanks!

@brfrn169 brfrn169 requested a review from komamitsu June 20, 2025 09:13
Copy link
Contributor

@komamitsu komamitsu left a comment

Choose a reason for hiding this comment

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

LGTM! 👍

@brfrn169 brfrn169 merged commit cd94a96 into master Jun 20, 2025
56 checks passed
@brfrn169 brfrn169 deleted the optimize-mutations-based-on-storage-mutation-atomicity-unit branch June 20, 2025 09:33
feeblefakie pushed a commit that referenced this pull request Jun 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants