Skip to content

Conversation

@gkevinzheng
Copy link
Contributor

@gkevinzheng gkevinzheng commented Dec 3, 2025

Changes:

  • Added a new property new_table_data_client (might need to be renamed) in the classic client for the new table data client
  • Added two hidden kwargs (client_info and disable_background_channel_refresh) to the new data client constructor to support the classic client.

Fixes #1157

@gkevinzheng gkevinzheng requested review from a team as code owners December 3, 2025 15:49
@product-auto-label product-auto-label bot added size: l Pull request size is large. api: bigtable Issues related to the googleapis/python-bigtable API. labels Dec 3, 2025
warnings.warn("pool_size no longer supported")
# set up client info headers for veneer library
self.client_info = DEFAULT_CLIENT_INFO
self.client_info = kwargs.get("client_info") or DEFAULT_CLIENT_INFO
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this can be `kwargs.get("client_info", DEFAULT_CLIENT_INFO)

_table_data_client = None
_table_admin_client = None
_instance_admin_client = None
_new_table_data_client = None
Copy link
Contributor

@daniel-sanche daniel-sanche Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. We should avoid calling this "new", because someday it will be old
  2. We don't need internal references for both the gapic client and the veneer data client, since the veneer holds a copy of the gapic.

So we can probably remove _table_data_client and _new_table_data_client, and just have a _data_client, that holds the veneer.

Then in def table_data_client(self):, return self._data_client._gapic_client

return self._instance_admin_client

@property
def new_table_data_client(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it makes sense to make a lazy-loader for this, but I don't think we want to expose this publicly

"""Getter for the new Data Table API.
TODO: Replace table_data_client with this implementation
when shimming legacy client is finished.
Copy link
Contributor

@daniel-sanche daniel-sanche Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's a goal. We should keep returning the gapic client, to keep things consistent (although I wouldn't actually expect many users to use the gapic data cleint direclty)

warnings.warn("pool_size no longer supported")
# set up client info headers for veneer library
self.client_info = DEFAULT_CLIENT_INFO
self.client_info = kwargs.get("client_info") or DEFAULT_CLIENT_INFO
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a comment above this. Maybe something like # client_info argument intended for internal use only, to support legacy client shim

# set up client info headers for veneer library
self.client_info = DEFAULT_CLIENT_INFO
self.client_info = kwargs.get("client_info") or DEFAULT_CLIENT_INFO
self.client_info.client_library_version = self._client_version()
Copy link
Contributor

@daniel-sanche daniel-sanche Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we want to set a different user agent for the shim... We'll probably want to know how many people are using the shim, to tell us when it can be removed

Maybe that means we should build our own client_info in the legacy constructor, and only attach this version string if none was passed in?

if "_client_info" in kwargs:
  # use client_info passed in from legacy client
  self.client_info = kwargs["_client_info"]
else:
  # set up client info headers for veneer library
  self.client_info = DEFAULT_CLIENT_INFO
  self.client_info.client_library_version = self._client_version()

disable_background_channel_refresh=True,
)
# should create background tasks for each channel
with mock.patch.object(client, "_ping_and_warm_instances", CrossSync.Mock()):
Copy link
Contributor

@daniel-sanche daniel-sanche Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to mock this? I think that was done in other tests as a crude way of disabling background refresh, but maybe that's not needed with the new argument

warnings.warn("pool_size no longer supported")
# set up client info headers for veneer library
self.client_info = DEFAULT_CLIENT_INFO
self.client_info = kwargs.get("client_info") or DEFAULT_CLIENT_INFO
Copy link
Contributor

@daniel-sanche daniel-sanche Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thought: We should use a leading underscore for these undocumented kwargs, to make it extra clear that users shouldn't use them if they find them in the code

return self._instance_admin_client

@property
def _data_client(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: having table_data_client and _data_client is a little weird. Since this is just for internal use, maybe we should use _veneer_data_client to be more specific?

project=self.project,
credentials=self._credentials,
client_options=self._client_options,
_client_info=self._client_info,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking we should customize the client_library_version like in the veneer client, so we can specifically see which requests are going through the shim. Maybe `f"{google.cloud.bigtable.version}-shim"?

What do you think? (Maybe ping Mattie?)

self.table_id,
)
if self._instance
else None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need if self._instance? It doesn't seem optional?

I don't think we'd ever want this to be None, right?

self._table_impl = (
self._instance._client._data_client.get_table(
self._instance.instance_id,
self.table_id,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing app_profile_id.

And maybe mutation_timeout should be mapped to default_mutate_rows_operation_timeout? Or maybe the timeout can just be passed in with each request. I guess you can leave that part for when you start looking at myutations

Copy link
Contributor

@daniel-sanche daniel-sanche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a couple suggestions, but let me know if I'm missing context

self.client_info = DEFAULT_CLIENT_INFO

# Private argument, for internal use only
self._is_legacy_client = bool(kwargs.get("_is_legacy_client", False))
Copy link
Contributor

@daniel-sanche daniel-sanche Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is just for customizing the client_version/background refresh, I'd prefer to avoid tracking this extra state.

We're already passing in a client_info object, so I'd say we should set the value in the legacy client before passing it in, and then do something like:

if kwargs.get("_client_info):
    self.client_info = kwargs.get("_client_info")
else:
  self.client_info = DEFAULT_CLIENT_INFO
  self.client_info.client_library_version = self._client_version()

not self._channel_refresh_task
and not self._emulator_host
and not self._is_closed.is_set()
and not self._is_legacy_client
Copy link
Contributor

@daniel-sanche daniel-sanche Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did like the old version, where there was a flag specifically for disabling background refresh. That could be useful for other purposes too, like testing

Enabling _is_legacy_client could come with other side-effects

@gkevinzheng gkevinzheng added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 19, 2025
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Dec 19, 2025
@gkevinzheng gkevinzheng merged commit 914006d into v3_staging Dec 19, 2025
6 of 9 checks passed
@gkevinzheng gkevinzheng deleted the data-client-init branch December 19, 2025 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: bigtable Issues related to the googleapis/python-bigtable API. size: l Pull request size is large.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants