This repository was archived by the owner on Mar 31, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 104
feat(db_api): add an ability to set ReadOnly/ReadWrite connection mode #475
Merged
Merged
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
4d7684e
feat(db_api): add an ability to set ReadOnly/ReadWrite connection mode
990fe3e
update the error message
2381c6d
update another error message
9cec921
don't check error messages with regexes
d6783b8
don't commit/rollback ReadOnly transactions
131a6fc
clear the transaction
e1a3db2
add read-only transactions data visibility test
4bd65e7
Apply suggestions from code review
4186609
add conditions for edge cases
0f3e716
Merge branch 'read_only_transactions' of https://github.com/q-logic/p…
8332d49
don't calc checksum for read-only transactions
61fc8ad
Merge branch 'master' into read_only_transactions
0be92af
use Snapshot for reads
3a72694
update docstrings
00887ff
Merge branch 'master' into read_only_transactions
b8eefed
Merge branch 'master' into read_only_transactions
de0e47e
use multi-use snapshots in !autocommit mode
9f1896a
return the read_only docstring back, erase excess unit test
4422ad5
Merge branch 'master' into read_only_transactions
larkee f11ea8c
erase excess ifs
9bafc76
Merge branch 'read_only_transactions' of https://github.com/q-logic/p…
783951f
Merge branch 'master' into read_only_transactions
d807a2d
add additional check into the snapshot_checkout() method
76e7caf
Merge branch 'master' into read_only_transactions
c61f212
add new style system test
9118987
Merge branch 'read_only_transactions' of https://github.com/q-logic/p…
70fc3ac
resolve conflict
22e5e73
don't use error message regexes
1cdccbe
erase excess import
9bb0ebc
Merge branch 'master' into read_only_transactions
ac8c4b2
refactor
ae9cd00
Merge branch 'main' into read_only_transactions
larkee 6973748
add unit test to check that read-only transactions are not retried
5dab169
Merge branch 'read_only_transactions' of https://github.com/q-logic/p…
5761c30
Merge branch 'main' into read_only_transactions
skuruppu afeb585
Merge branch 'main' into read_only_transactions
skuruppu 1ccb387
Merge branch 'main' into read_only_transactions
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ | |
| from google.api_core.gapic_v1.client_info import ClientInfo | ||
| from google.cloud import spanner_v1 as spanner | ||
| from google.cloud.spanner_v1.session import _get_retry_delay | ||
| from google.cloud.spanner_v1.snapshot import Snapshot | ||
|
|
||
| from google.cloud.spanner_dbapi._helpers import _execute_insert_heterogenous | ||
| from google.cloud.spanner_dbapi._helpers import _execute_insert_homogenous | ||
|
|
@@ -50,15 +51,31 @@ class Connection: | |
|
|
||
| :type database: :class:`~google.cloud.spanner_v1.database.Database` | ||
| :param database: The database to which the connection is linked. | ||
|
|
||
| :type read_only: bool | ||
| :param read_only: | ||
| Flag to indicate that the connection may only execute queries and no update or DDL statements. | ||
| If True, the connection will use a single use read-only transaction with strong timestamp | ||
| bound for each new statement, and will immediately see any changes that have been committed by | ||
| any other transaction. | ||
| If autocommit is false, the connection will automatically start a new multi use read-only transaction | ||
| with strong timestamp bound when the first statement is executed. This read-only transaction will be | ||
| used for all subsequent statements until either commit() or rollback() is called on the connection. The | ||
| read-only transaction will read from a consistent snapshot of the database at the time that the | ||
| transaction started. This means that the transaction will not see any changes that have been | ||
| committed by other transactions since the start of the read-only transaction. Commit or rolling back | ||
| the read-only transaction is semantically the same, and only indicates that the read-only transaction | ||
| should end a that a new one should be started when the next statement is executed. | ||
| """ | ||
|
|
||
| def __init__(self, instance, database): | ||
| def __init__(self, instance, database, read_only=False): | ||
| self._instance = instance | ||
| self._database = database | ||
| self._ddl_statements = [] | ||
|
|
||
| self._transaction = None | ||
| self._session = None | ||
| self._snapshot = None | ||
| # SQL statements, which were executed | ||
| # within the current transaction | ||
| self._statements = [] | ||
|
|
@@ -69,6 +86,7 @@ def __init__(self, instance, database): | |
| # this connection should be cleared on the | ||
| # connection close | ||
| self._own_pool = True | ||
| self._read_only = read_only | ||
|
|
||
| @property | ||
| def autocommit(self): | ||
|
|
@@ -123,6 +141,30 @@ def instance(self): | |
| """ | ||
| return self._instance | ||
|
|
||
| @property | ||
| def read_only(self): | ||
| """Flag: the connection can be used only for database reads. | ||
|
|
||
| Returns: | ||
| bool: | ||
| True if the connection may only be used for database reads. | ||
| """ | ||
| return self._read_only | ||
|
|
||
| @read_only.setter | ||
| def read_only(self, value): | ||
| """`read_only` flag setter. | ||
|
|
||
| Args: | ||
| value (bool): True for ReadOnly mode, False for ReadWrite. | ||
| """ | ||
| if self.inside_transaction: | ||
| raise ValueError( | ||
| "Connection read/write mode can't be changed while a transaction is in progress. " | ||
| "Commit or rollback the current transaction and try again." | ||
| ) | ||
| self._read_only = value | ||
|
|
||
| def _session_checkout(self): | ||
| """Get a Cloud Spanner session from the pool. | ||
|
|
||
|
|
@@ -231,6 +273,22 @@ def transaction_checkout(self): | |
|
|
||
| return self._transaction | ||
|
|
||
| def snapshot_checkout(self): | ||
| """Get a Cloud Spanner snapshot. | ||
|
|
||
| Initiate a new multi-use snapshot, if there is no snapshot in | ||
| this connection yet. Return the existing one otherwise. | ||
|
|
||
| :rtype: :class:`google.cloud.spanner_v1.snapshot.Snapshot` | ||
| :returns: A Cloud Spanner snapshot object, ready to use. | ||
| """ | ||
| if self.read_only and not self.autocommit: | ||
| if not self._snapshot: | ||
| self._snapshot = Snapshot(self._session_checkout(), multi_use=True) | ||
| self._snapshot.begin() | ||
|
|
||
| return self._snapshot | ||
|
|
||
| def _raise_if_closed(self): | ||
| """Helper to check the connection state before running a query. | ||
| Raises an exception if this connection is closed. | ||
|
|
@@ -259,14 +317,18 @@ def commit(self): | |
|
|
||
| This method is non-operational in autocommit mode. | ||
| """ | ||
| self._snapshot = None | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would make more sense to add the This also applies to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| if self._autocommit: | ||
| warnings.warn(AUTOCOMMIT_MODE_WARNING, UserWarning, stacklevel=2) | ||
| return | ||
|
|
||
| self.run_prior_DDL_statements() | ||
| if self.inside_transaction: | ||
| try: | ||
| self._transaction.commit() | ||
| if not self.read_only: | ||
| self._transaction.commit() | ||
|
|
||
| self._release_session() | ||
| self._statements = [] | ||
| except Aborted: | ||
|
|
@@ -279,10 +341,14 @@ def rollback(self): | |
| This is a no-op if there is no active transaction or if the connection | ||
| is in autocommit mode. | ||
| """ | ||
| self._snapshot = None | ||
|
|
||
| if self._autocommit: | ||
| warnings.warn(AUTOCOMMIT_MODE_WARNING, UserWarning, stacklevel=2) | ||
| elif self._transaction: | ||
| self._transaction.rollback() | ||
| if not self.read_only: | ||
| self._transaction.rollback() | ||
|
|
||
| self._release_session() | ||
| self._statements = [] | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.