feat(sqlalchemy-spanner): wire timeout execution option through to DBAPI Connection.timeout#16468
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a timeout execution option for Spanner connections and ensures the timeout is cleared during connection resets. It also includes unit tests for these changes. Review feedback recommends using unittest assertion methods for improved failure messages and moving local imports to the top of the file to comply with PEP 8.
| assert dbapi_conn.staleness is None | ||
| assert dbapi_conn.read_only is False | ||
| assert dbapi_conn.timeout is None |
There was a problem hiding this comment.
In classes that inherit from unittest.TestCase, it is a best practice to use the provided assertion methods (e.g., self.assertIsNone, self.assertFalse, self.assertEqual). These methods provide more descriptive failure messages, which can make debugging failing tests easier. Please consider replacing the assert statements throughout this file with their unittest counterparts.
| assert dbapi_conn.staleness is None | |
| assert dbapi_conn.read_only is False | |
| assert dbapi_conn.timeout is None | |
| self.assertIsNone(dbapi_conn.staleness) | |
| self.assertFalse(dbapi_conn.read_only) | |
| self.assertIsNone(dbapi_conn.timeout) |
| from google.cloud.sqlalchemy_spanner.sqlalchemy_spanner import ( | ||
| SpannerExecutionContext, | ||
| ) |
There was a problem hiding this comment.
To align with PEP 8 guidelines and improve code organization, please move this import to the top of the file. Local imports are generally reserved for cases like avoiding circular dependencies or handling optional packages, which doesn't seem to be the case here. You can add SpannerExecutionContext to the existing import from google.cloud.sqlalchemy_spanner.sqlalchemy_spanner on line 21.
References
- PEP 8 states that imports should always be put at the top of the file, just after any module comments and docstrings, and before module globals and constants. This makes it clear what modules the file requires. (link)
…API Connection.timeout Add timeout handling to SpannerExecutionContext.pre_exec() and reset_connection() so that users can set a per-statement gRPC deadline via execution_options(timeout=N). Depends on googleapis/python-spanner#1534 for the DBAPI Connection.timeout property. Fixes googleapis#16467
5832882 to
1d95d61
Compare
Summary
Add
timeouthandling toSpannerExecutionContext.pre_exec()andreset_connection()so that users can set a per-statement gRPC deadline via SQLAlchemy'sexecution_options(timeout=N).Fixes #16467
Background
The Spanner SQLAlchemy dialect reads execution options in
SpannerExecutionContext.pre_exec()and forwards them to the DBAPI connection. Currently handled options:read_only,staleness,request_priority,transaction_tag,request_tag,ignore_transaction_warnings. Thetimeoutoption is not handled.The underlying Spanner client's
_SnapshotBase.execute_sql()accepts atimeoutparameter that controls the gRPC deadline forExecuteStreamingSql. Without it, all queries use the gRPC default of 3600 seconds.Timeline
pre_exec()handlesread_onlyandstalenessd976fda(PR #269)request_priorityadded topre_exec()5bd5076(PR #494)transaction_tagandrequest_tagadded topre_exec()66c32a4(PR #503)ignore_transaction_warningsadded topre_exec()timeoutwas never added topre_exec()orreset_connection()Each new execution option was added incrementally.
timeoutwas not included in any of these additions.Changes
sqlalchemy_spanner.pytimeouthandling inpre_exec(): readsself.execution_options.get("timeout")and setsself._dbapi_connection.connection.timeouttimeoutreset inreset_connection(): setsdbapi_conn.timeout = Nonewhen connection returns to poolUsage
Tests
Added 5 unit tests in
tests/unit/test_sqlalchemy_spanner.py:test_reset_connection_clears_timeout— verifiesreset_connectionsetstimeout = Nonetest_reset_connection_with_wrapper— verifies reset works through connection wrappertest_pre_exec_sets_timeout— verifiestimeoutis forwarded to DBAPI connectiontest_pre_exec_no_timeout_leaves_connection_unchanged— verifiestimeoutis not set when absent from optionstest_pre_exec_timeout_with_other_options— verifiestimeoutcoexists withread_onlyandrequest_priorityPrerequisites
This change depends on the DBAPI
Connectionsupporting atimeoutproperty: googleapis/python-spanner#1534 (PR: googleapis/python-spanner#1535)Related
_SnapshotBase.execute_sql()has acceptedtimeout=since November 2018 (PR [Spanner] Add timeout + retry settings to Sessions/Snapshots #6536 in the original monorepo)googleapis/python-spanner-sqlalchemy(now archived)