Skip to content

Commit 9f2ed92

Browse files
authored
chore(spanner): deprecate experimental host option/parameter to replace with Spanner Omni (googleapis#17246)
This PR deprecates the `experimental_host` parameter across public `Client`, `AsyncClient`, and DB-API `connect` interfaces, introducing the new unified `instance_type` parameter. The `instance_type` option supports `cloud` and `omni` connection types; setting the type to `cloud` currently acts as a no-op, whereas establishing a connection to a Spanner Omni instance strictly requires setting `instance_type="omni"`. Internally, all private `_experimental_host` attributes have been purged and refactored to dynamically resolve endpoints via client properties, all public docs-strings have been updated to describe this behavior. Refer to discussion: [Spanner Client Library Configuration for Omni](https://docs.google.com/document/d/1n-rsHkNAEwQbaICHKlFhPl3NN8a3QuOknn_tEhaowZQ/edit?resourcekey=0-LnCt0jSFXCoSuVttphq3FQ&tab=t.0) Similar PR in java-spanner googleapis/google-cloud-java#13236
1 parent 7813ca4 commit 9f2ed92

26 files changed

Lines changed: 497 additions & 159 deletions

packages/google-cloud-spanner/google/cloud/spanner_dbapi/connection.py

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,7 @@ def connect(
747747
ca_certificate=None,
748748
client_certificate=None,
749749
client_key=None,
750+
instance_type=None,
750751
**kwargs,
751752
):
752753
"""Creates a connection to a Google Cloud Spanner database.
@@ -802,26 +803,29 @@ def connect(
802803
resource.
803804
804805
:type experimental_host: str
805-
:param experimental_host: (Optional) The endpoint for a spanner experimental host deployment.
806-
This is intended only for experimental host spanner endpoints.
806+
:param experimental_host: (Deprecated) Use `client_options` with `api_endpoint` and `instance_type="omni"` instead.
807+
808+
:type instance_type: str
809+
:param instance_type: (Optional) The type of Spanner instance to connect to.
810+
Supported values are `"cloud"` or `"omni"`. Connecting to Spanner Omni requires setting instance_type=`"omni"`.
807811
808812
:type use_plain_text: bool
809813
:param use_plain_text: (Optional) Whether to use plain text for the connection.
810-
This is intended only for experimental host spanner endpoints.
814+
This is intended only for Spanner Omni endpoints.
811815
If not set, the default behavior is to use TLS.
812816
813817
:type ca_certificate: str
814818
:param ca_certificate: (Optional) The path to the CA certificate file used for TLS connection.
815-
This is intended only for experimental host spanner endpoints.
816-
This is mandatory if the experimental_host requires a TLS connection.
819+
This is intended only for Spanner Omni endpoints.
820+
This is mandatory if Spanner Omni requires a TLS connection.
817821
:type client_certificate: str
818822
:param client_certificate: (Optional) The path to the client certificate file used for mTLS connection.
819-
This is intended only for experimental host spanner endpoints.
820-
This is mandatory if the experimental_host requires an mTLS connection.
823+
This is intended only for Spanner Omni endpoints.
824+
This is mandatory if Spanner Omni requires an mTLS connection.
821825
:type client_key: str
822826
:param client_key: (Optional) The path to the client key file used for mTLS connection.
823-
This is intended only for experimental host spanner endpoints.
824-
This is mandatory if the experimental_host requires an mTLS connection.
827+
This is intended only for Spanner Omni endpoints.
828+
This is mandatory if Spanner Omni requires an mTLS connection.
825829
"""
826830
if client is None:
827831
client_info = ClientInfo(
@@ -840,10 +844,48 @@ def connect(
840844
client_options = None
841845
if isinstance(credentials, AnonymousCredentials):
842846
client_options = kwargs.get("client_options")
847+
843848
if experimental_host is not None:
849+
warnings.warn(
850+
"experimental_host is deprecated. Please use client_options with api_endpoint instead, along with instance_type='omni'.",
851+
DeprecationWarning,
852+
stacklevel=2,
853+
)
854+
instance_type = "omni"
855+
856+
if instance_type is not None:
857+
instance_type = instance_type.lower()
858+
if instance_type not in ("cloud", "omni"):
859+
raise ValueError("instance_type must be one of 'cloud' or 'omni'")
860+
861+
if instance_type == "omni":
862+
host_endpoint = experimental_host
863+
if not host_endpoint and "client_options" in kwargs:
864+
opts = kwargs["client_options"]
865+
if hasattr(opts, "api_endpoint"):
866+
host_endpoint = opts.api_endpoint
867+
elif isinstance(opts, dict):
868+
host_endpoint = opts.get("api_endpoint")
869+
870+
if not host_endpoint:
871+
raise ValueError(
872+
"Host must be set for connecting to Spanner Omni instances"
873+
)
874+
844875
project = "default"
845876
credentials = AnonymousCredentials()
846-
client_options = ClientOptions(api_endpoint=experimental_host)
877+
client_options = kwargs.get("client_options")
878+
if client_options is None:
879+
client_options = ClientOptions(api_endpoint=host_endpoint)
880+
elif isinstance(client_options, dict):
881+
client_options = client_options.copy()
882+
client_options["api_endpoint"] = host_endpoint
883+
else:
884+
import copy
885+
886+
client_options = copy.copy(client_options)
887+
client_options.api_endpoint = host_endpoint
888+
847889
client = spanner.Client(
848890
project=project,
849891
credentials=credentials,
@@ -854,6 +896,7 @@ def connect(
854896
ca_certificate=ca_certificate,
855897
client_certificate=client_certificate,
856898
client_key=client_key,
899+
instance_type=instance_type,
857900
)
858901
else:
859902
if project is not None and client.project != project:

packages/google-cloud-spanner/google/cloud/spanner_v1/_async/_helpers.py

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,21 @@ async def _retry(
6767
retries += 1
6868

6969

70-
def _create_experimental_host_transport(
70+
def _create_spanner_omni_transport(
7171
transport_factory,
72-
experimental_host,
72+
host,
7373
use_plain_text,
7474
ca_certificate,
7575
client_certificate,
7676
client_key,
7777
interceptors=None,
7878
):
79-
"""Creates an experimental host transport for Spanner in async mode.
79+
"""Creates a Spanner Omni transport in async mode.
8080
8181
Args:
8282
transport_factory (type): The transport class to instantiate (e.g.
8383
`SpannerGrpcAsyncIOTransport`).
84-
experimental_host (str): The endpoint for the experimental host.
84+
host (str): The endpoint for Spanner Omni.
8585
use_plain_text (bool): Whether to use a plain text (insecure) connection.
8686
ca_certificate (str): Path to the CA certificate file for TLS.
8787
client_certificate (str): Path to the client certificate file for mTLS.
@@ -99,9 +99,7 @@ def _create_experimental_host_transport(
9999

100100
channel = None
101101
if use_plain_text:
102-
channel = grpc.aio.insecure_channel(
103-
target=experimental_host, interceptors=interceptors
104-
)
102+
channel = grpc.aio.insecure_channel(target=host, interceptors=interceptors)
105103
elif ca_certificate:
106104
with open(ca_certificate, "rb") as f:
107105
ca_cert = f.read()
@@ -121,11 +119,37 @@ def _create_experimental_host_transport(
121119
)
122120
else:
123121
ssl_creds = grpc.ssl_channel_credentials(root_certificates=ca_cert)
124-
channel = grpc.aio.secure_channel(
125-
experimental_host, ssl_creds, interceptors=interceptors
126-
)
122+
channel = grpc.aio.secure_channel(host, ssl_creds, interceptors=interceptors)
127123
else:
128124
raise ValueError(
129-
"TLS/mTLS connection requires ca_certificate to be set for experimental_host"
125+
"TLS/mTLS connection requires ca_certificate to be set for Spanner Omni"
130126
)
131127
return transport_factory(channel=channel, credentials=AnonymousCredentials())
128+
129+
130+
def _create_experimental_host_transport(
131+
transport_factory,
132+
experimental_host,
133+
use_plain_text,
134+
ca_certificate,
135+
client_certificate,
136+
client_key,
137+
interceptors=None,
138+
):
139+
"""Deprecated alias for _create_spanner_omni_transport."""
140+
import warnings
141+
142+
warnings.warn(
143+
"_create_experimental_host_transport is deprecated. Please use _create_spanner_omni_transport instead.",
144+
DeprecationWarning,
145+
stacklevel=2,
146+
)
147+
return _create_spanner_omni_transport(
148+
transport_factory,
149+
experimental_host,
150+
use_plain_text,
151+
ca_certificate,
152+
client_certificate,
153+
client_key,
154+
interceptors=interceptors,
155+
)

packages/google-cloud-spanner/google/cloud/spanner_v1/_async/client.py

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ def _initialize_metrics(project, credentials):
174174
)
175175

176176

177+
class InstanceType:
178+
CLOUD = "cloud"
179+
OMNI = "omni"
180+
EMULATOR = "emulator"
181+
182+
177183
@CrossSync.convert_class(
178184
docstring_format_vars={
179185
"experimental_api": (
@@ -254,9 +260,11 @@ class Client(ClientWithProject):
254260
:param default_transaction_options: (Optional) Default options to use for all transactions.
255261
256262
:type experimental_host: str
257-
:param experimental_host: (Optional) The endpoint for a spanner experimental host deployment.
258-
This is intended only for experimental host spanner endpoints.
259-
If set, this will override the `api_endpoint` in `client_options`.
263+
:param experimental_host: (Deprecated) Use `client_options` with `api_endpoint` and `instance_type="omni"` instead.
264+
265+
:type instance_type: str
266+
:param instance_type: (Optional) The type of Spanner instance to connect to.
267+
Supported values are `"cloud"` or `"omni"`. Connecting to Spanner Omni requires setting instance_type="omni".
260268
261269
:type disable_builtin_metrics: bool
262270
:param disable_builtin_metrics: (Optional) Default False. Set to True to disable
@@ -293,9 +301,9 @@ def __init__(
293301
ca_certificate=None,
294302
client_certificate=None,
295303
client_key=None,
304+
instance_type=None,
296305
):
297306
self._emulator_host = _get_spanner_emulator_host()
298-
self._experimental_host = experimental_host
299307
self._use_plain_text = use_plain_text
300308
self._ca_certificate = ca_certificate
301309
self._client_certificate = client_certificate
@@ -308,10 +316,38 @@ def __init__(
308316
else:
309317
self._client_options = client_options
310318

319+
host_endpoint = None
320+
if experimental_host is not None:
321+
warnings.warn(
322+
"experimental_host is deprecated. Please use client_options with api_endpoint instead, along with instance_type='omni'.",
323+
DeprecationWarning,
324+
stacklevel=2,
325+
)
326+
instance_type = "omni"
327+
host_endpoint = experimental_host
328+
329+
if instance_type is not None:
330+
instance_type = instance_type.lower()
331+
if instance_type not in ("cloud", "omni"):
332+
raise ValueError("instance_type must be one of 'cloud' or 'omni'")
333+
self._instance_type = instance_type
334+
311335
if self._emulator_host:
312336
credentials = AnonymousCredentials()
313-
elif self._experimental_host:
314-
# For all experimental host endpoints project is default
337+
elif self._instance_type == "omni":
338+
if not host_endpoint:
339+
if self._client_options:
340+
if hasattr(self._client_options, "api_endpoint"):
341+
host_endpoint = self._client_options.api_endpoint
342+
elif isinstance(self._client_options, dict):
343+
host_endpoint = self._client_options.get("api_endpoint")
344+
345+
if not host_endpoint:
346+
raise ValueError(
347+
"Host must be set for connecting to Spanner Omni instances"
348+
)
349+
350+
# For all spanner omni endpoints project is default
315351
project = "default"
316352
self._use_plain_text = use_plain_text
317353
self._ca_certificate = ca_certificate
@@ -372,8 +408,8 @@ def __init__(
372408
self._host = "spanner.googleapis.com"
373409
if self._emulator_host:
374410
self._host = self._emulator_host
375-
elif self._experimental_host:
376-
self._host = self._experimental_host
411+
elif self._instance_type == "omni":
412+
self._host = host_endpoint
377413
elif self._client_options and self._client_options.api_endpoint:
378414
self._host = self._client_options.api_endpoint
379415

@@ -414,6 +450,14 @@ def credentials(self):
414450
"""
415451
return self._credentials
416452

453+
@property
454+
def instance_type(self):
455+
"""Getter for client's instance type.
456+
457+
:rtype: str
458+
:returns: The instance type of the client."""
459+
return self._instance_type
460+
417461
@property
418462
def project_name(self):
419463
"""Project name to be used with Spanner APIs.
@@ -449,28 +493,28 @@ def instance_admin_api(self):
449493
transport=transport,
450494
)
451495

452-
elif self._experimental_host:
496+
elif self._instance_type == "omni":
453497
from google.cloud.spanner_v1._async._helpers import (
454-
_create_experimental_host_transport as _create_experimental_host_transport_async,
498+
_create_spanner_omni_transport as _create_spanner_omni_transport_async,
455499
)
456500
from google.cloud.spanner_v1._helpers import (
457-
_create_experimental_host_transport as _create_experimental_host_transport_sync,
501+
_create_spanner_omni_transport as _create_spanner_omni_transport_sync,
458502
)
459503

460504
if CrossSync.is_async:
461-
transport = _create_experimental_host_transport_async(
505+
transport = _create_spanner_omni_transport_async(
462506
InstanceAdminGrpcTransport,
463-
self._experimental_host,
507+
self._host,
464508
self._use_plain_text,
465509
self._ca_certificate,
466510
self._client_certificate,
467511
self._client_key,
468512
)
469513

470514
else:
471-
transport = _create_experimental_host_transport_sync(
515+
transport = _create_spanner_omni_transport_sync(
472516
InstanceAdminGrpcTransport,
473-
self._experimental_host,
517+
self._host,
474518
self._use_plain_text,
475519
self._ca_certificate,
476520
self._client_certificate,
@@ -507,28 +551,28 @@ def database_admin_api(self):
507551
transport=transport,
508552
)
509553

510-
elif self._experimental_host:
554+
elif self._instance_type == "omni":
511555
from google.cloud.spanner_v1._async._helpers import (
512-
_create_experimental_host_transport as _create_experimental_host_transport_async,
556+
_create_spanner_omni_transport as _create_spanner_omni_transport_async,
513557
)
514558
from google.cloud.spanner_v1._helpers import (
515-
_create_experimental_host_transport as _create_experimental_host_transport_sync,
559+
_create_spanner_omni_transport as _create_spanner_omni_transport_sync,
516560
)
517561

518562
if CrossSync.is_async:
519-
transport = _create_experimental_host_transport_async(
563+
transport = _create_spanner_omni_transport_async(
520564
DatabaseAdminGrpcTransport,
521-
self._experimental_host,
565+
self._host,
522566
self._use_plain_text,
523567
self._ca_certificate,
524568
self._client_certificate,
525569
self._client_key,
526570
)
527571

528572
else:
529-
transport = _create_experimental_host_transport_sync(
573+
transport = _create_spanner_omni_transport_sync(
530574
DatabaseAdminGrpcTransport,
531-
self._experimental_host,
575+
self._host,
532576
self._use_plain_text,
533577
self._ca_certificate,
534578
self._client_certificate,
@@ -682,7 +726,6 @@ def instance(
682726
self._emulator_host,
683727
labels,
684728
processing_units,
685-
self._experimental_host,
686729
)
687730

688731
@CrossSync.convert

0 commit comments

Comments
 (0)