Skip to content

Conversation

@brfrn169
Copy link
Collaborator

@brfrn169 brfrn169 commented Jun 13, 2025

Description

This PR adds further optimizations for Consensus Commit.

It introduces a single-operation mode, a mode for transactions executed without explicitly beginning a transaction, such as DistributedTransactionManager.get(), DistributedTransactionManager.scan(), and DistributedTransactionManager.insert().

Details of the optimizations are provided in the inline comments.

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

N/A

@brfrn169 brfrn169 requested a review from Copilot June 13, 2025 06:37
@brfrn169 brfrn169 self-assigned this Jun 13, 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 introduces additional optimizations for Consensus Commit by updating transaction APIs and tests to use a more flexible begin method with extra flags (readOnly and singleOperation). Key changes include:

  • Refactoring internal transaction creation to use a new begin(txId, isolation, readOnly, singleOperation) signature.
  • Updating numerous test classes to verify the new multi-argument begin calls.
  • Enhancing snapshot read logic and CRUD handler behavior with single-operation mode optimizations.

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
integration-test/src/main/java/com/scalar/db/transaction/singlecrudoperation/SingleCrudOperationTransactionIntegrationTestBase.java Added a disabled test for multiple mutations in single CRUD operations.
integration-test/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitSpecificIntegrationTestBase.java Introduced various new tests using the updated begin method with additional parameters.
core/src/test/java/... Updated tests to verify the new begin(anyString(), eq(Isolation.SNAPSHOT), …) signatures.
core/src/main/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommitManager.java Refactored begin methods with extra flags and updated visibility of internal methods.
core/src/main/java/com/scalar/db/transaction/consensuscommit/Snapshot.java Changed mergeResult method access from private to public.
core/src/main/java/com/scalar/db/transaction/consensuscommit/CrudHandler.java Added a singleOperation flag and helper methods for conditional snapshot reading.
core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitManager.java Updated transaction begin methods and introduced a beginSingleOperation helper.
Comments suppressed due to low confidence (4)

core/src/main/java/com/scalar/db/transaction/consensuscommit/CrudHandler.java:57

  • [nitpick] A 'singleOperation' flag has been introduced to control snapshot read behavior; consider adding or updating documentation to clearly explain its purpose and effects on CRUD operations.
private final boolean singleOperation;

core/src/main/java/com/scalar/db/transaction/consensuscommit/Snapshot.java:238

  • [nitpick] Changing the visibility of mergeResult from private to public alters encapsulation; ensure that this exposure is intentional and is well documented for API consumers.
public Optional<TransactionResult> mergeResult(Key key, Optional<TransactionResult> result)

core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitManager.java:254

  • The new beginSingleOperation method is used to optimize single-operation reads; verify that the corresponding tests fully cover scenarios where snapshot reads are bypassed as intended.
private DistributedTransaction beginSingleOperation(boolean readOnly) {

core/src/main/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommitManager.java:151

  • Changing the visibility of this begin method to private may affect testability—ensure that tests relying on internal behavior are updated accordingly.
private TwoPhaseCommitTransaction begin(String txId, Isolation isolation) {

Comment on lines 54 to 57
// Whether the transaction is in single-operation mode or not. Single-operation mode refers to
// executing a CRUD operation directly through `DistributedTransactionManager` without explicitly
// beginning a transaction.
private final boolean singleOperation;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Introduced single-operation mode, a mode for transactions executed without explicitly beginning a transaction, such as DistributedTransactionManager.get(), DistributedTransactionManager.scan(), and DistributedTransactionManager.insert().

Comment on lines 291 to 330
private boolean isSnapshotReadRequired() {
// In single-operation mode, we don't need snapshot read
return !singleOperation;
}

private boolean isValidationOrSnapshotReadRequired() {
return snapshot.isValidationRequired() || isSnapshotReadRequired();
}

private void putIntoGetSetInSnapshot(Get get, Optional<TransactionResult> result) {
// If neither validation nor snapshot read is required, we don't need to put the result into
// the get set
if (isValidationOrSnapshotReadRequired()) {
snapshot.putIntoGetSet(get, result);
}
}

private void putIntoScanSetInSnapshot(
Scan scan, LinkedHashMap<Snapshot.Key, TransactionResult> results) {
// If neither validation nor snapshot read is required, we don't need to put the results into
// the scan set
if (isValidationOrSnapshotReadRequired()) {
snapshot.putIntoScanSet(scan, results);
}
}

private void putIntoScannerSetInSnapshot(
Scan scan, LinkedHashMap<Snapshot.Key, TransactionResult> results) {
// if validation is not required, we don't need to put the results into the scanner set
if (snapshot.isValidationRequired()) {
snapshot.putIntoScannerSet(scan, results);
}
}

private void verifyNoOverlap(Scan scan, Map<Snapshot.Key, TransactionResult> results) {
// In read-only mode, we don't need to verify the overlap
if (!readOnly) {
// In either read-only mode or single-operation mode, we don't need to verify the overlap
if (!readOnly && !singleOperation) {
snapshot.verifyNoOverlap(scan, results);
}
}
Copy link
Collaborator Author

@brfrn169 brfrn169 Jun 13, 2025

Choose a reason for hiding this comment

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

Here is the main part of the optimizations.

The optimizations can be summarized as follows:

  • If snapshot reads and serializable validation are not required, we can avoid adding records to the getSet and scanSet, thereby reducing memory usage. This is because they are only used for snapshot reads and serializable validation.
  • If serializable validation is not required, we can skip adding ScannerInfo to the scannerSet, since it is used solely for that purpose.
  • In addition to read-only mode, we can also skip verifying overlaps between the scan and the records in the writeSet and deleteSet in single-operation mode.

Copy link
Contributor

Choose a reason for hiding this comment

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

If a single-crud operation is a self-join reading the same record more than twice, is it OK not to use snapshot read?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The single-operation mode here means the mode for transactions that are executed without explicitly beginning a transaction, such as the following methods:

DistributedTransactionManger transactionManager = ...;

Optional<Result> result = transactionManager.get(Get.newBuilder()...);

List<Result> results = transactionManager.scan(Scan.newBuilder()...);

Scanner scanner = transactionManager.getScanner(Scan.newBuilder()...);

transactionManager.insert(Insert.newBuilder()...);

transactionManager.upsert(Upsert.newBuilder()...);

transactionManager.update(Update.newBuilder()...);

transactionManager.delete(Delete.newBuilder()...);

transactionManager.mutate(Arrays.asList(Delete.newBuilder()..., Update.newBuilder()...));

In these cases, the transaction is committed immediately after the operation is done. So we don't need snapshot reads in such cases. Does this answer your question?

Copy link
Contributor

Choose a reason for hiding this comment

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

@brfrn169 Probably, I am confused with single-crud operation transaction and single-operation mode.
If they are different things, it's pretty confusing so I think we should change the name.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, okay. This isn’t related to single CRUD operation transactions — it’s specific to Consensus Commit.

Do you have any suggestions for what we should call transactions that are executed without explicitly beginning a transaction?

@brfrn169 brfrn169 changed the title Adds additional optimizations for Consensus Commit Adds further optimizations for Consensus Commit Jun 13, 2025
@brfrn169 brfrn169 changed the title Adds further optimizations for Consensus Commit Add further optimizations for Consensus Commit Jun 13, 2025
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.

@brfrn169 We don't need to confirm the number of operations in a transaction is only 1 at the commit phase, if the single operation feature is enabled on the transaction?

private final Scanner scanner;

private final LinkedHashMap<Snapshot.Key, TransactionResult> results = new LinkedHashMap<>();
private final LinkedHashMap<Snapshot.Key, TransactionResult> results;
Copy link
Contributor

Choose a reason for hiding this comment

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

This should have @Nullable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 61b2860. Thanks!

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.

Left some comments and questions. PTAL!

.map(
r ->
new FilteredResult(r, originalProjections, metadata, isIncludeMetadataEnabled));
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

How about handling null/empty conjunction in the mergeResult method to unify the operations in the if/else clauses?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in cefe83b. Thanks!

Comment on lines 291 to 330
private boolean isSnapshotReadRequired() {
// In single-operation mode, we don't need snapshot read
return !singleOperation;
}

private boolean isValidationOrSnapshotReadRequired() {
return snapshot.isValidationRequired() || isSnapshotReadRequired();
}

private void putIntoGetSetInSnapshot(Get get, Optional<TransactionResult> result) {
// If neither validation nor snapshot read is required, we don't need to put the result into
// the get set
if (isValidationOrSnapshotReadRequired()) {
snapshot.putIntoGetSet(get, result);
}
}

private void putIntoScanSetInSnapshot(
Scan scan, LinkedHashMap<Snapshot.Key, TransactionResult> results) {
// If neither validation nor snapshot read is required, we don't need to put the results into
// the scan set
if (isValidationOrSnapshotReadRequired()) {
snapshot.putIntoScanSet(scan, results);
}
}

private void putIntoScannerSetInSnapshot(
Scan scan, LinkedHashMap<Snapshot.Key, TransactionResult> results) {
// if validation is not required, we don't need to put the results into the scanner set
if (snapshot.isValidationRequired()) {
snapshot.putIntoScannerSet(scan, results);
}
}

private void verifyNoOverlap(Scan scan, Map<Snapshot.Key, TransactionResult> results) {
// In read-only mode, we don't need to verify the overlap
if (!readOnly) {
// In either read-only mode or single-operation mode, we don't need to verify the overlap
if (!readOnly && !singleOperation) {
snapshot.verifyNoOverlap(scan, results);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

If a single-crud operation is a self-join reading the same record more than twice, is it OK not to use snapshot read?

@brfrn169
Copy link
Collaborator Author

brfrn169 commented Jun 16, 2025

We don't need to confirm the number of operations in a transaction is only 1 at the commit phase, if the single operation feature is enabled on the transaction?

@komamitsu The single-operation flag is set to true only for transactions that are executed without explicitly beginning a transaction, such as the following methods:

DistributedTransactionManger transactionManager = ...;

Optional<Result> result = transactionManager.get(Get.newBuilder()...);

List<Result> results = transactionManager.scan(Scan.newBuilder()...);

Scanner scanner = transactionManager.getScanner(Scan.newBuilder()...);

transactionManager.insert(Insert.newBuilder()...);

transactionManager.upsert(Upsert.newBuilder()...);

transactionManager.update(Update.newBuilder()...);

transactionManager.delete(Delete.newBuilder()...);

transactionManager.mutate(Arrays.asList(Delete.newBuilder()..., Update.newBuilder()...));

In these cases, the number of operations in a transaction is always 1 at the commit phase (except for mutate, but we can handle it similarly). So we don’t need to explicitly check the number of operations.

@brfrn169 brfrn169 requested a review from komamitsu June 16, 2025 04:40
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 requested a review from feeblefakie June 16, 2025 05:40
parallelExecutor,
readOnly);
readOnly,
singleOperation);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
singleOperation);
oneOperation);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 042d1e1. Thanks!

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!

@brfrn169 brfrn169 requested a review from feeblefakie June 17, 2025 04:52
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!

@brfrn169 brfrn169 merged commit dfb51f5 into master Jun 18, 2025
55 checks passed
@brfrn169 brfrn169 deleted the further-optimizations-for-consensus-commit branch June 18, 2025 03:36
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