@@ -35,6 +35,8 @@ import (
3535 "google.golang.org/grpc/metadata"
3636)
3737
38+ const directpathEnvVar = "CBT_ENABLE_DIRECTPATH"
39+
3840// Client is a client for reading and writing data to tables in an instance.
3941//
4042// A Client is safe to use concurrently, except for its Close method.
@@ -72,6 +74,9 @@ type ClientConfig struct {
7274 // DisableConnectionRecycler disables the automatic preemptive refresh of connection.
7375 // Preemptive connection is default to true
7476 DisableConnectionRecycler bool
77+
78+ // DisableDirectAccess disables direct access by default.
79+ DisableDirectAccess bool
7580}
7681
7782// MetricsProvider is a wrapper for built in metrics meter provider
@@ -129,6 +134,7 @@ func NewClientWithConfig(ctx context.Context, project, instance string, config C
129134 var directPathOptions = []option.ClientOption {
130135 internaloption .EnableDirectPath (true ),
131136 internaloption .EnableDirectPathXds (),
137+ internaloption .AllowHardBoundTokens ("ALTS" ),
132138 }
133139
134140 // Allow non-default service account in DirectPath.
@@ -155,10 +161,11 @@ func NewClientWithConfig(ctx context.Context, project, instance string, config C
155161 // as CFE/GFE will call RLS with gslb target type
156162 // only TD calls the RLS with grpc target type
157163 // and we evaluate the directAccess option after that.
158- directAccessMD := createFeatureFlagsMD (metricsTracerFactory .enabled , disableRetryInfo , true )
164+
165+ allowDirectAccess := isDirectAccessEnabled (config )
166+ directAccessMD := createFeatureFlagsMD (metricsTracerFactory .enabled , disableRetryInfo , allowDirectAccess )
159167
160168 var connPool gtransport.ConnPool
161- var connPoolErr error
162169 var dsm * btransport.DynamicScaleMonitor
163170 var connRecycler * btransport.ConnectionRecycler
164171
@@ -171,7 +178,18 @@ func NewClientWithConfig(ctx context.Context, project, instance string, config C
171178 }
172179 }
173180 var connPoolSize int
174- if enableBigtableConnPool {
181+ if ! enableBigtableConnPool {
182+ // Use the regular ConnPool
183+ // For regular ConnPool the Direct Access is off by default so we need to check the env var again.
184+ if enabled , _ := strconv .ParseBool (os .Getenv (directpathEnvVar )); enabled {
185+ o = append (o , directPathOptions ... )
186+ }
187+ regConnPool , err := gtransport .DialPool (ctx , o ... )
188+ if err != nil {
189+ return nil , err
190+ }
191+ connPool = regConnPool
192+ } else { // Use the BigtableConnPool
175193 uResolver , err := internaloption .NewUnsafeResolver (o ... )
176194 if err != nil {
177195 // just fallback
@@ -186,18 +204,29 @@ func NewClientWithConfig(ctx context.Context, project, instance string, config C
186204
187205 fullInstanceName := fmt .Sprintf ("projects/%s/instances/%s" , project , instance )
188206
189- directAccessDialerOptions := make ([]option.ClientOption , len (o ))
190- copy (directAccessDialerOptions , o )
191- directAccessDialerOptions = append (directAccessDialerOptions , directPathOptions ... )
192- // enable hard bound tokens by default
193- directAccessDialerOptions = append (directAccessDialerOptions , internaloption .AllowHardBoundTokens ("ALTS" ))
207+ var poolOpts []btransport.BigtableChannelPoolOption
208+ poolOpts = append (poolOpts ,
209+ btransport .WithInstanceName (fullInstanceName ),
210+ btransport .WithAppProfile (config .AppProfile ),
211+ btransport .WithFeatureFlagsMetadata (directAccessMD ),
212+ btransport .WithMetricsReporterConfig (btopt .DefaultMetricsReporterConfig ()),
213+ btransport .WithMeterProvider (metricsTracerFactory .otelMeterProvider ),
214+ btransport .WithDirectAccessFeatureFlagsMetadata (directAccessMD ),
215+ )
194216
195- directAccessDialer := func () (* btransport.BigtableConn , error ) {
196- grpcConn , err := gtransport .Dial (ctx , directAccessDialerOptions ... )
197- if err != nil {
198- return nil , err
217+ // Only setup DirectPath dialers if not disabled by config/env
218+ if allowDirectAccess {
219+ directAccessDialerOptions := make ([]option.ClientOption , len (o ))
220+ copy (directAccessDialerOptions , o )
221+ directAccessDialerOptions = append (directAccessDialerOptions , directPathOptions ... )
222+ directAccessDialer := func () (* btransport.BigtableConn , error ) {
223+ grpcConn , err := gtransport .Dial (ctx , directAccessDialerOptions ... )
224+ if err != nil {
225+ return nil , err
226+ }
227+ return btransport .NewBigtableConn (grpcConn ), nil
199228 }
200- return btransport .NewBigtableConn ( grpcConn ), nil
229+ poolOpts = append ( poolOpts , btransport .WithDirectAccessDialer ( directAccessDialer ))
201230 }
202231
203232 btPool , err := btransport .NewBigtableChannelPool (ctx ,
@@ -212,51 +241,28 @@ func NewClientWithConfig(ctx context.Context, project, instance string, config C
212241 },
213242 clientCreationTimestamp ,
214243 // options
215- btransport .WithInstanceName (fullInstanceName ),
216- btransport .WithAppProfile (config .AppProfile ),
217- btransport .WithFeatureFlagsMetadata (directAccessMD ),
218- btransport .WithMetricsReporterConfig (btopt .DefaultMetricsReporterConfig ()),
219- btransport .WithMeterProvider (metricsTracerFactory .otelMeterProvider ),
220- btransport .WithDirectAccessFeatureFlagsMetadata (directAccessMD ),
221- btransport .WithDirectAccessDialer (directAccessDialer ),
244+ poolOpts ... ,
222245 )
223-
224246 if err != nil {
225- connPoolErr = err
226- } else {
227- connPool = btPool
228-
229- // Validate dynamic config early if enabled
230- if ! config .DisableDynamicChannelPool {
231- if err := btransport .ValidateDynamicConfig (btopt .DefaultDynamicChannelPoolConfig (), defaultBigtableConnPoolSize ); err != nil {
232- return nil , fmt .Errorf ("invalid DynamicChannelPoolConfig: %w" , err )
233- }
247+ return nil , err
248+ }
234249
235- dsm = btransport .NewDynamicScaleMonitor (btopt .DefaultDynamicChannelPoolConfig (), btPool )
236- dsm .Start (ctx ) // Start the monitor's background goroutine
237- }
238- // connection recyler.
239- if ! config .DisableConnectionRecycler {
240- connRecycler = btransport .NewConnectionRecycler (btopt .DefaultConnectionRecycleConfig (), btPool )
241- connRecycler .Start (ctx ) // Start the monitor's background goroutine
250+ connPool = btPool
251+
252+ // Validate dynamic config early if enabled
253+ if ! config .DisableDynamicChannelPool {
254+ if err := btransport .ValidateDynamicConfig (btopt .DefaultDynamicChannelPoolConfig (), defaultBigtableConnPoolSize ); err != nil {
255+ return nil , fmt .Errorf ("invalid DynamicChannelPoolConfig: %w" , err )
242256 }
243257
258+ dsm = btransport .NewDynamicScaleMonitor (btopt .DefaultDynamicChannelPoolConfig (), btPool )
259+ dsm .Start (ctx ) // Start the monitor's background goroutine
244260 }
245-
246- } else {
247- enableDirectAccess , _ := strconv .ParseBool (os .Getenv ("CBT_ENABLE_DIRECTPATH" ))
248- if enableDirectAccess {
249- o = append (o , directPathOptions ... )
250- if disableBoundToken , _ := strconv .ParseBool (os .Getenv ("CBT_DISABLE_DIRECTPATH_BOUND_TOKEN" )); ! disableBoundToken {
251- o = append (o , internaloption .AllowHardBoundTokens ("ALTS" ))
252- }
261+ // connection recyler.
262+ if ! config .DisableConnectionRecycler {
263+ connRecycler = btransport .NewConnectionRecycler (btopt .DefaultConnectionRecycleConfig (), btPool )
264+ connRecycler .Start (ctx ) // Start the monitor's background goroutine
253265 }
254- // use to regular ConnPool
255- connPool , connPoolErr = gtransport .DialPool (ctx , o ... )
256- }
257-
258- if connPoolErr != nil {
259- return nil , connPoolErr
260266 }
261267
262268 return & Client {
@@ -400,3 +406,11 @@ func (c *Client) newBuiltinMetricsTracer(ctx context.Context, table string, isSt
400406 mt := c .metricsTracerFactory .createBuiltinMetricsTracer (ctx , table , isStreaming )
401407 return & mt
402408}
409+
410+ func isDirectAccessEnabled (config ClientConfig ) bool {
411+ if os .Getenv (directpathEnvVar ) == "" {
412+ return ! config .DisableDirectAccess
413+ }
414+ res , _ := strconv .ParseBool (os .Getenv (directpathEnvVar ))
415+ return res
416+ }
0 commit comments