Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit d155f8a

Browse files
committed
set up channel interceptions
1 parent ac8dbe4 commit d155f8a

8 files changed

Lines changed: 60 additions & 34 deletions

File tree

google/cloud/bigtable/data/_async/client.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
cast,
2020
Any,
2121
AsyncIterable,
22+
Callable,
2223
Optional,
2324
Set,
2425
Sequence,
@@ -104,6 +105,7 @@
104105
else:
105106
from typing import Iterable # noqa: F401
106107
from grpc import insecure_channel
108+
from grpc import intercept_channel
107109
from google.cloud.bigtable_v2.services.bigtable.transports import BigtableGrpcTransport as TransportType # type: ignore
108110
from google.cloud.bigtable_v2.services.bigtable import BigtableClient as GapicClient # type: ignore
109111
from google.cloud.bigtable.data._sync_autogen.mutations_batcher import _MB_SIZE
@@ -206,7 +208,7 @@ def __init__(
206208
credentials = google.auth.credentials.AnonymousCredentials()
207209
if project is None:
208210
project = _DEFAULT_BIGTABLE_EMULATOR_CLIENT
209-
211+
self._metrics_interceptor = MetricInterceptorType()
210212
# initialize client
211213
ClientWithProject.__init__(
212214
self,
@@ -234,7 +236,6 @@ def __init__(
234236
self._executor: concurrent.futures.ThreadPoolExecutor | None = (
235237
concurrent.futures.ThreadPoolExecutor() if not CrossSync.is_async else None
236238
)
237-
self._interceptor = MetricInterceptorType()
238239
if self._emulator_host is None:
239240
# attempt to start background channel refresh tasks
240241
try:
@@ -263,12 +264,23 @@ def _build_grpc_channel(self, *args, **kwargs) -> AsyncSwappableChannel:
263264
Returns:
264265
a custom wrapped swappable channel
265266
"""
267+
create_channel_fn: Callable[[], Channel]
266268
if self._emulator_host is not None:
267269
# emulators use insecure channel
268270
create_channel_fn = partial(insecure_channel, self._emulator_host)
269-
else:
271+
elif CrossSync.is_async:
270272
create_channel_fn = partial(TransportType.create_channel, *args, **kwargs)
271-
return AsyncSwappableChannel(create_channel_fn)
273+
else:
274+
# attach sync interceptors in create_channel_fn
275+
create_channel_fn = lambda: intercept_channel(
276+
TransportType.create_channel(*args, **kwargs), self._metrics_interceptor
277+
)
278+
new_channel = AsyncSwappableChannel(create_channel_fn)
279+
if CrossSync.is_async:
280+
# attach async interceptors
281+
new_channel._unary_unary_interceptors.append(self._metrics_interceptor)
282+
new_channel._unary_stream_interceptors.append(self._metrics_interceptor)
283+
return new_channel
272284

273285
@staticmethod
274286
def _client_version() -> str:
@@ -922,24 +934,12 @@ def __init__(
922934
)
923935

924936
self._metrics = BigtableClientSideMetricsController(
925-
client._interceptor,
937+
client._metrics_interceptor,
926938
project_id=self.client.project,
927939
instance_id=instance_id,
928940
table_id=table_id,
929941
app_profile_id=app_profile_id,
930942
)
931-
# TODO: simplify interceptors
932-
if CrossSync.is_async:
933-
client.transport.grpc_channel._unary_unary_interceptors.append(
934-
self._metrics.interceptor
935-
)
936-
client.transport.grpc_channel._unary_stream_interceptors.append(
937-
self._metrics.interceptor
938-
)
939-
else:
940-
client.transport.grpc_channel = intercept_channel(
941-
self._metrics.interceptor, client.transport.grpc_channel
942-
)
943943

944944
try:
945945
self._register_instance_future = CrossSync.create_task(

google/cloud/bigtable/data/_async/metrics_interceptor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License
14+
from __future__ import annotations
1415

1516
import time
1617
from typing import Any, Callable

google/cloud/bigtable/data/_sync_autogen/client.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# This file is automatically generated by CrossSync. Do not edit manually.
1818

1919
from __future__ import annotations
20-
from typing import cast, Any, Optional, Set, Sequence, TYPE_CHECKING
20+
from typing import cast, Any, Callable, Optional, Set, Sequence, TYPE_CHECKING
2121
import abc
2222
import time
2323
import warnings
@@ -76,6 +76,7 @@
7676
from google.cloud.bigtable.data._cross_sync import CrossSync
7777
from typing import Iterable
7878
from grpc import insecure_channel
79+
from grpc import intercept_channel
7980
from google.cloud.bigtable_v2.services.bigtable.transports import (
8081
BigtableGrpcTransport as TransportType,
8182
)
@@ -147,6 +148,7 @@ def __init__(
147148
credentials = google.auth.credentials.AnonymousCredentials()
148149
if project is None:
149150
project = _DEFAULT_BIGTABLE_EMULATOR_CLIENT
151+
self._metrics_interceptor = MetricInterceptorType()
150152
ClientWithProject.__init__(
151153
self,
152154
credentials=credentials,
@@ -172,7 +174,6 @@ def __init__(
172174
if not CrossSync._Sync_Impl.is_async
173175
else None
174176
)
175-
self._interceptor = MetricInterceptorType()
176177
if self._emulator_host is None:
177178
try:
178179
self._start_background_channel_refresh()
@@ -196,11 +197,15 @@ def _build_grpc_channel(self, *args, **kwargs) -> SwappableChannel:
196197
- **kwargs: keyword arguments passed by the gapic layer to create a new channel with
197198
Returns:
198199
a custom wrapped swappable channel"""
200+
create_channel_fn: Callable[[], Channel]
199201
if self._emulator_host is not None:
200202
create_channel_fn = partial(insecure_channel, self._emulator_host)
201203
else:
202-
create_channel_fn = partial(TransportType.create_channel, *args, **kwargs)
203-
return SwappableChannel(create_channel_fn)
204+
create_channel_fn = lambda: intercept_channel(
205+
TransportType.create_channel(*args, **kwargs), self._metrics_interceptor
206+
)
207+
new_channel = SwappableChannel(create_channel_fn)
208+
return new_channel
204209

205210
@staticmethod
206211
def _client_version() -> str:
@@ -723,15 +728,12 @@ def __init__(
723728
default_retryable_errors or ()
724729
)
725730
self._metrics = BigtableClientSideMetricsController(
726-
client._interceptor,
731+
client._metrics_interceptor,
727732
project_id=self.client.project,
728733
instance_id=instance_id,
729734
table_id=table_id,
730735
app_profile_id=app_profile_id,
731736
)
732-
client.transport.grpc_channel = intercept_channel(
733-
self._metrics.interceptor, client.transport.grpc_channel
734-
)
735737
try:
736738
self._register_instance_future = CrossSync._Sync_Impl.create_task(
737739
self.client._register_instance,

google/cloud/bigtable/data/_sync_autogen/metrics_interceptor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License
1414

15-
1615
# This file is automatically generated by CrossSync. Do not edit manually.
1716

17+
from __future__ import annotations
1818
from functools import wraps
1919
from google.cloud.bigtable.data._metrics.data_model import (
2020
OPERATION_INTERCEPTOR_METADATA_KEY,

tests/system/data/test_system_async.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -292,23 +292,32 @@ async def test_channel_refresh(self, table_id, instance_id, temp_rows):
292292
async with client.get_table(instance_id, table_id) as table:
293293
rows = await table.read_rows({})
294294
channel_wrapper = client.transport.grpc_channel
295-
first_channel = client.transport.grpc_channel._channel
295+
first_channel = channel_wrapper._channel
296296
assert len(rows) == 2
297297
await CrossSync.sleep(2)
298298
rows_after_refresh = await table.read_rows({})
299299
assert len(rows_after_refresh) == 2
300300
assert client.transport.grpc_channel is channel_wrapper
301-
assert client.transport.grpc_channel._channel is not first_channel
302-
# ensure gapic's logging interceptor is still active
301+
updated_channel = channel_wrapper._channel
302+
assert channel_wrapper._channel is not first_channel
303+
# ensure interceptors are kept (gapic's logging interceptor, and metric interceptor)
303304
if CrossSync.is_async:
304-
interceptors = (
305-
client.transport.grpc_channel._channel._unary_unary_interceptors
305+
unary_interceptors = (
306+
updated_channel._unary_unary_interceptors
306307
)
307-
assert GapicInterceptor in [type(i) for i in interceptors]
308+
assert len(unary_interceptors) == 2
309+
assert GapicInterceptor in [type(i) for i in unary_interceptors]
310+
assert client._metrics_interceptor in unary_interceptors
311+
stream_interceptors = (
312+
updated_channel._unary_stream_interceptors
313+
)
314+
assert len(stream_interceptors) == 1
315+
assert client._metrics_interceptor in stream_interceptors
308316
else:
309317
assert isinstance(
310318
client.transport._logged_channel._interceptor, GapicInterceptor
311319
)
320+
assert updated_channel._interceptor == client._metrics_interceptor
312321
finally:
313322
await client.close()
314323

tests/system/data/test_system_autogen.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,16 +235,18 @@ def test_channel_refresh(self, table_id, instance_id, temp_rows):
235235
with client.get_table(instance_id, table_id) as table:
236236
rows = table.read_rows({})
237237
channel_wrapper = client.transport.grpc_channel
238-
first_channel = client.transport.grpc_channel._channel
238+
first_channel = channel_wrapper._channel
239239
assert len(rows) == 2
240240
CrossSync._Sync_Impl.sleep(2)
241241
rows_after_refresh = table.read_rows({})
242242
assert len(rows_after_refresh) == 2
243243
assert client.transport.grpc_channel is channel_wrapper
244-
assert client.transport.grpc_channel._channel is not first_channel
244+
updated_channel = channel_wrapper._channel
245+
assert channel_wrapper._channel is not first_channel
245246
assert isinstance(
246247
client.transport._logged_channel._interceptor, GapicInterceptor
247248
)
249+
assert updated_channel._interceptor == client._metrics_interceptor
248250
finally:
249251
client.close()
250252

tests/unit/data/_async/test_client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,22 @@
5454
from google.cloud.bigtable.data._async._swappable_channel import (
5555
AsyncSwappableChannel,
5656
)
57+
from google.cloud.bigtable.data._async.metrics_interceptor import AsyncBigtableMetricsInterceptor
5758

5859
CrossSync.add_mapping("grpc_helpers", grpc_helpers_async)
5960
CrossSync.add_mapping("SwappableChannel", AsyncSwappableChannel)
61+
CrossSync.add_mapping("MetricsInterceptor", AsyncBigtableMetricsInterceptor)
6062
else:
6163
from google.api_core import grpc_helpers
6264
from google.cloud.bigtable.data._sync_autogen.client import Table # noqa: F401
6365
from google.cloud.bigtable.data._sync_autogen._swappable_channel import (
6466
SwappableChannel,
6567
)
68+
from google.cloud.bigtable.data._sync_autogen.metrics_interceptor import BigtableMetricsInterceptor
6669

6770
CrossSync.add_mapping("grpc_helpers", grpc_helpers)
6871
CrossSync.add_mapping("SwappableChannel", SwappableChannel)
72+
CrossSync.add_mapping("MetricsInterceptor", BigtableMetricsInterceptor)
6973

7074
__CROSS_SYNC_OUTPUT__ = "tests.unit.data._sync_autogen.test_client"
7175

@@ -113,6 +117,7 @@ async def test_ctor(self):
113117
assert not client._active_instances
114118
assert client._channel_refresh_task is not None
115119
assert client.transport._credentials == expected_credentials
120+
assert isinstance(client._metrics_interceptor, CrossSync.MetricsInterceptor)
116121
await client.close()
117122

118123
@CrossSync.pytest

tests/unit/data/_sync_autogen/test_client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,13 @@
4646
)
4747
from google.api_core import grpc_helpers
4848
from google.cloud.bigtable.data._sync_autogen._swappable_channel import SwappableChannel
49+
from google.cloud.bigtable.data._sync_autogen.metrics_interceptor import (
50+
BigtableMetricsInterceptor,
51+
)
4952

5053
CrossSync._Sync_Impl.add_mapping("grpc_helpers", grpc_helpers)
5154
CrossSync._Sync_Impl.add_mapping("SwappableChannel", SwappableChannel)
55+
CrossSync._Sync_Impl.add_mapping("MetricsInterceptor", BigtableMetricsInterceptor)
5256

5357

5458
@CrossSync._Sync_Impl.add_mapping_decorator("TestBigtableDataClient")
@@ -84,6 +88,9 @@ def test_ctor(self):
8488
assert not client._active_instances
8589
assert client._channel_refresh_task is not None
8690
assert client.transport._credentials == expected_credentials
91+
assert isinstance(
92+
client._metrics_interceptor, CrossSync._Sync_Impl.MetricsInterceptor
93+
)
8794
client.close()
8895

8996
def test_ctor_super_inits(self):

0 commit comments

Comments
 (0)