9292 from google .cloud .bigtable_v2 .services .bigtable .transports import (
9393 BigtableGrpcAsyncIOTransport as TransportType ,
9494 )
95+ from google .cloud .bigtable_v2 .services .bigtable .transports .grpc_asyncio import (
96+ _LoggingClientAIOInterceptor
97+ )
9598 from google .cloud .bigtable .data ._async .mutations_batcher import _MB_SIZE
99+ from google .cloud .bigtable .data ._async .replaceable_channel import _AsyncReplaceableChannel
96100else :
97101 from typing import Iterable # noqa: F401
98102 from grpc import insecure_channel
@@ -182,7 +186,6 @@ def __init__(
182186 client_options = cast (
183187 Optional [client_options_lib .ClientOptions ], client_options
184188 )
185- custom_channel = None
186189 self ._emulator_host = os .getenv (BIGTABLE_EMULATOR )
187190 if self ._emulator_host is not None :
188191 warnings .warn (
@@ -191,11 +194,11 @@ def __init__(
191194 stacklevel = 2 ,
192195 )
193196 # use insecure channel if emulator is set
194- custom_channel = insecure_channel (self ._emulator_host )
195197 if credentials is None :
196198 credentials = google .auth .credentials .AnonymousCredentials ()
197199 if project is None :
198200 project = _DEFAULT_BIGTABLE_EMULATOR_CLIENT
201+
199202 # initialize client
200203 ClientWithProject .__init__ (
201204 self ,
@@ -208,7 +211,7 @@ def __init__(
208211 client_options = client_options ,
209212 client_info = self .client_info ,
210213 transport = lambda * args , ** kwargs : TransportType (
211- * args , ** kwargs , channel = custom_channel
214+ * args , ** kwargs , channel = self . _build_grpc_channel
212215 ),
213216 )
214217 self ._is_closed = CrossSync .Event ()
@@ -235,6 +238,23 @@ def __init__(
235238 stacklevel = 2 ,
236239 )
237240
241+ def _build_grpc_channel (self , * args , ** kwargs ):
242+ if self ._emulator_host is not None :
243+ # emulators use insecure channel
244+ return insecure_channel (self ._emulator_host )
245+ create_channel_fn = partial (TransportType .create_channel , * args , ** kwargs )
246+ # interceptors are handled differently between async and sync, because of differences in the grpc and gapic layers
247+ if CrossSync .is_async :
248+ # for async, add interceptors to the creation function
249+ create_channel_fn = partial (create_channel_fn , interceptors = [_LoggingClientAIOInterceptor ()])
250+ return _AsyncReplaceableChannel (create_channel_fn )
251+ else :
252+ # for sync, chain interceptors using grpc.channel.intercept
253+ # LoggingClientInterceptor not needed, since it is chained in the gapic layer
254+ return TransportType .create_channel (* args , ** kwargs )
255+
256+
257+
238258 @staticmethod
239259 def _client_version () -> str :
240260 """
@@ -376,32 +396,11 @@ async def _manage_channel(
376396 break
377397 start_timestamp = time .monotonic ()
378398 # prepare new channel for use
379- # TODO: refactor to avoid using internal references: https://github.com/googleapis/python-bigtable/issues/1094
380- old_channel = self .transport .grpc_channel
381- new_channel = self .transport .create_channel ()
382- if CrossSync .is_async :
383- new_channel ._unary_unary_interceptors .append (
384- self .transport ._interceptor
385- )
386- else :
387- new_channel = intercept_channel (
388- new_channel , self .transport ._interceptor
389- )
399+ new_channel = self .transport .grpc_channel .create_channel ()
390400 await self ._ping_and_warm_instances (channel = new_channel )
391401 # cycle channel out of use, with long grace window before closure
392- self .transport ._grpc_channel = new_channel
393- self .transport ._logged_channel = new_channel
394- # invalidate caches
395- self .transport ._stubs = {}
396- self .transport ._prep_wrapped_messages (self .client_info )
397- # give old_channel a chance to complete existing rpcs
398- if CrossSync .is_async :
399- await old_channel .close (grace_period )
400- else :
401- if grace_period :
402- self ._is_closed .wait (grace_period ) # type: ignore
403- old_channel .close () # type: ignore
404- # subtract thed time spent waiting for the channel to be replaced
402+ await self .transport .grpc_channel .replace_channel (new_channel , grace_period )
403+ # subtract the time spent waiting for the channel to be replaced
405404 next_refresh = random .uniform (refresh_interval_min , refresh_interval_max )
406405 next_sleep = max (next_refresh - (time .monotonic () - start_timestamp ), 0 )
407406
0 commit comments