-
Notifications
You must be signed in to change notification settings - Fork 88
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: Session leaks #957
base: main
Are you sure you want to change the base?
feat: Session leaks #957
Changes from 28 commits
9d83ef2
4d16548
920eb55
aff17b1
f271251
799589e
38f71b8
54e1717
7ed07c6
57309de
c38b15d
d678841
286e1f4
be6eee3
86a2552
c33f930
cb748d8
1b72281
ac9e0e4
ff8674d
5a09450
a75f147
9540c0a
c419204
b735e5e
2e489c4
1dccb4f
0e04dde
ee5b3a3
f8c258d
9331df2
33988c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,12 @@ | |
+ "numeric has a whole component with precision {}" | ||
) | ||
|
||
LONG_RUNNING_TRANSACTION_ERR_MSG = "Transaction has been closed as it was running for more than 60 minutes. If transaction is expected to run long, run as batch or partitioned DML." | ||
|
||
# Constants | ||
DELETE_LONG_RUNNING_TRANSACTION_FREQUENCY_SEC = 120 | ||
DELETE_LONG_RUNNING_TRANSACTION_THRESHOLD_SEC = 3600 | ||
Comment on lines
+44
to
+45
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. Do we have a reason for not preferring minutes? From a readability point of view, minutes will be more consistent across languages. What do you think? 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. Python sleep library expects seconds to be passed and does not support this. Also its helpful in overriding these properties to a smaller value for tests. Seconds looks file to me. |
||
|
||
|
||
def _try_to_coerce_bytes(bytestring): | ||
"""Try to coerce a byte string into the right thing based on Python | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -113,6 +113,11 @@ class Database(object): | |
is `True` to log commit statistics. If not passed, a logger | ||
will be created when needed that will log the commit statistics | ||
to stdout. | ||
|
||
:type close_inactive_transactions: boolean | ||
:param close_inactive_transactions: (Optional) If set to True, the database will automatically close inactive transactions that have been running for longer than 60 minutes which may cause session leaks. | ||
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. nit: same here for the 60 minutes |
||
By default, this is set to False. | ||
|
||
:type encryption_config: | ||
:class:`~google.cloud.spanner_admin_database_v1.types.EncryptionConfig` | ||
or :class:`~google.cloud.spanner_admin_database_v1.types.RestoreDatabaseEncryptionConfig` | ||
|
@@ -142,6 +147,7 @@ def __init__( | |
ddl_statements=(), | ||
pool=None, | ||
logger=None, | ||
close_inactive_transactions=False, | ||
encryption_config=None, | ||
database_dialect=DatabaseDialect.DATABASE_DIALECT_UNSPECIFIED, | ||
database_role=None, | ||
|
@@ -160,6 +166,7 @@ def __init__( | |
self._default_leader = None | ||
self.log_commit_stats = False | ||
self._logger = logger | ||
self._close_inactive_transactions = close_inactive_transactions | ||
self._encryption_config = encryption_config | ||
self._database_dialect = database_dialect | ||
self._database_role = database_role | ||
|
@@ -366,7 +373,7 @@ def enable_drop_protection(self, value): | |
def logger(self): | ||
"""Logger used by the database. | ||
|
||
The default logger will log commit stats at the log level INFO using | ||
The default logger will log at the log level INFO using | ||
`sys.stderr`. | ||
|
||
:rtype: :class:`logging.Logger` or `None` | ||
|
@@ -381,6 +388,14 @@ def logger(self): | |
self._logger.addHandler(ch) | ||
return self._logger | ||
|
||
@property | ||
def close_inactive_transactions(self): | ||
"""Whether the database has has closing inactive transactions enabled. Default: False. | ||
:rtype: bool | ||
:returns: True if closing inactive transactions is enabled, else False. | ||
""" | ||
return self._close_inactive_transactions | ||
|
||
@property | ||
def spanner_api(self): | ||
"""Helper for session-related API calls.""" | ||
|
@@ -647,7 +662,7 @@ def execute_partitioned_dml( | |
) | ||
|
||
def execute_pdml(): | ||
with SessionCheckout(self._pool) as session: | ||
with SessionCheckout(self._pool, isLongRunning=True) as session: | ||
surbhigarg92 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
txn = api.begin_transaction( | ||
session=session.name, options=txn_options, metadata=metadata | ||
|
@@ -1020,6 +1035,7 @@ def __enter__(self): | |
"""Begin ``with`` block.""" | ||
session = self._session = self._database._pool.get() | ||
batch = self._batch = Batch(session) | ||
self._session._transaction = batch | ||
if self._request_options.transaction_tag: | ||
batch.transaction_tag = self._request_options.transaction_tag | ||
return batch | ||
|
@@ -1038,7 +1054,9 @@ def __exit__(self, exc_type, exc_val, exc_tb): | |
"CommitStats: {}".format(self._batch.commit_stats), | ||
extra={"commit_stats": self._batch.commit_stats}, | ||
) | ||
self._database._pool.put(self._session) | ||
if self._batch._session is not None: | ||
self._database._pool.put(self._session) | ||
self._session._transaction = None | ||
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. Query - I think we are doing this to flush the transaction context which we newly created. Is this the best place to flush. Are there any other place where existing properties are flushed? 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. We are also deleting this in commit and rollback methods, apart from all these methods. https://github.com/googleapis/python-spanner/blob/main/google/cloud/spanner_v1/transaction.py#L181 Also In case of retry due to abort errors. https://github.com/googleapis/python-spanner/blob/main/google/cloud/spanner_v1/session.py#L388 |
||
|
||
|
||
class SnapshotCheckout(object): | ||
|
@@ -1062,22 +1080,27 @@ class SnapshotCheckout(object): | |
def __init__(self, database, **kw): | ||
self._database = database | ||
self._session = None | ||
self._snapshot = None | ||
self._kw = kw | ||
|
||
def __enter__(self): | ||
"""Begin ``with`` block.""" | ||
session = self._session = self._database._pool.get() | ||
return Snapshot(session, **self._kw) | ||
self._snapshot = Snapshot(session, **self._kw) | ||
self._session._transaction = self._snapshot | ||
return self._snapshot | ||
|
||
def __exit__(self, exc_type, exc_val, exc_tb): | ||
"""End ``with`` block.""" | ||
if isinstance(exc_val, NotFound): | ||
# If NotFound exception occurs inside the with block | ||
# then we validate if the session still exists. | ||
if not self._session.exists(): | ||
self._session = self._database._pool._new_session() | ||
self._session.create() | ||
self._database._pool.put(self._session) | ||
if self._snapshot._session is not None: | ||
surbhigarg92 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if isinstance(exc_val, NotFound): | ||
# If NotFound exception occurs inside the with block | ||
# then we validate if the session still exists. | ||
if not self._session.exists(): | ||
self._session = self._database._pool._new_session() | ||
self._session.create() | ||
self._database._pool.put(self._session) | ||
self._session._transaction = None | ||
|
||
|
||
class BatchSnapshot(object): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I thought we were removing the exact 60 minute thing here?