Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(google-cloud-spanner): Implement "Inline Begin Transaction" #54

Merged
merged 100 commits into from Sep 5, 2023

Conversation

SandeepTuniki
Copy link
Contributor

@SandeepTuniki SandeepTuniki commented Aug 1, 2023

Implements "Inline Begin Transaction" optimisation for Ruby Spanner.

Overview

Cloud Spanner allows users to query the data in several ways, including transactions. A transaction allows users to execute a series of statements as a single atomic unit, and allows them to rollback the changes in case of errors.

Most Cloud Spanner *Request APIs include a TransactionSelector field for queries. The TransactionSelector field can be used within queries to specify an existing transaction ID or start a new transaction.

Currently, we make an explicit BeginTransaction rpc to create a transaction in the backend, and get the transaction ID as the response. We pass the ID in the TransactionSelector field in the subsequent operations within the transaction.

This process has the following drawbacks:

  • Startup overhead - The client sets up the environment for transactions at the start of the client instantiation. This incurs an overhead in the instantiation time.

  • Prepared transactions - As we cannot predict in advance how many transactions the user will need, we must prepare several transactions during client instantiation. This is done by maintaining two separate session pools in a specific ratio, one for read-only transactions and the other for read-write transactions. As the user accesses this pool of sessions, the client must rebalance the ratio of the two stacks.

  • Transaction latency - The client makes an additional RPC to create a transaction. When the session pool runs out of available transactions, the client has to create more transactions. Both these factors increase the latency.

To address these drawbacks, we can defer the creation of transactions until the user executes the first query within the transaction. This is known as inlining the “begin transaction” call.

Implementation details

Below are the the high-level list of changes done throughout the codebase:

Pool class:

  • Stop creating transactions in advance, and instead only maintain sessions in the pool.
  • Remove the write_ratio field and the corresponding code that maintains transactions through write_ratio.
  • Simplified maintenance:
    • Introduce new fields sessions_available and sessions_in_use to simplify the maintenance of session pool.
    • The new fields replace the previous fields all_sessions, session_stack and transaction_stack.

Transaction class:

  • Inline the transaction creation to the first operation within the transaction.
  • Add thread safety mechanisms to ensure that the transaction creation isn't initiated multiple times by concurrent operations.
  • Add some helper methods (ex: existing_transaction?, safe_begin_transaction, safe_execute etc).

Client class:

  • Refactor the .transaction method to handle inlining of transaction creation.
  • Introduce helper methods (ex: check_and_propagate_err()) to reduce code complexity issues identified by Rubocop.

BatchUpdateResults class:

  • New class introduced to facilitate easier handling of tx.batch_update() API's response.

Project class:

  • Mark the write_ratio field as deprecated during client initialisation.

Results class:

  • Add a helper method transaction to facilitate extraction of a transaction from the results metadata.

Session class:

  • Add a helper method to create an empty transaction object (i.e; without gapic object), to facilitate ILB of a transaction.

Service class:

  • Refactor some code & move the processing logic (ex: batch update) to other classes.

Tests

Unit tests

  • Several unit tests are added to test the inlining of transactions.
  • Many existing unit tests are modified to handle ILB.
  • All the obsolete unit tests in the session pool which are related to write_ratio & transaction_stack are removed, and some are modified to fit the new mechanism of the session pool.

Acceptance tets

  • All existing acceptance should pass, since ILB is an internal optimisation.
  • Some existing acceptance tests are modified for easier testing.
  • Few tests are added to handle edge cases.

@SandeepTuniki SandeepTuniki changed the title Test ILB PR feat: Implement "Inline Begin" in transactions Aug 27, 2023
@SandeepTuniki SandeepTuniki changed the title feat: Implement "Inline Begin" in transactions feat(google-cloud-spanner): Implement "Inline Begin" in transactions Aug 27, 2023
@SandeepTuniki
Copy link
Contributor Author

SandeepTuniki commented Aug 27, 2023

Below are the benchmarking results after inlining the "begin transaction" rpc.

Scenario 1_ Client init + query

Scenario 2_ Query excluding client init

@SandeepTuniki SandeepTuniki changed the title feat(google-cloud-spanner): Implement "Inline Begin" in transactions feat(google-cloud-spanner): Implement "Inline Begin Transaction" Aug 27, 2023
@SandeepTuniki SandeepTuniki marked this pull request as ready for review August 28, 2023 00:45
@SandeepTuniki SandeepTuniki requested review from a team as code owners August 28, 2023 00:45
Copy link
Member

@dazuma dazuma left a comment

Choose a reason for hiding this comment

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

I think the logic looks good now. I have a few more recommendations on internal documentation, just to help with maintainability of the concurrency and synchronization.

@SandeepTuniki SandeepTuniki merged commit 49ac4d9 into main Sep 5, 2023
16 of 17 checks passed
@SandeepTuniki SandeepTuniki deleted the ilb-with-write-ratio branch September 5, 2023 06:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants