Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@
_MUTATE_ROW_METHOD = "/google.bigtable.v2.Bigtable/MutateRow"
_READ_ROWS_METHOD = "/google.bigtable.v2.Bigtable/ReadRows"

# Message caps on the UDS channel. gRPC defaults both directions to 4 MiB, a
# limit meant for a channel that crosses a network. This one doesn't leave the
# machine, and all the default does on this hop is turn an otherwise-fine call
# into RESOURCE_EXHAUSTED before it ever reaches the daemon's own client. Each
# direction is instead set to the matching service-side limit, so the UDS leg is
# never the narrower of the two and an oversized call fails where it should --
# at the service, with the service's own error.
#
# Inbound: 256 MiB, the hard limit on the combined size of all values in a
# single row, which bounds the largest response the service can produce.
# Outbound: 100 MB, the request size the service itself accepts.
_MAX_RECEIVE_MESSAGE_LENGTH = 256 * 1024 * 1024
_MAX_SEND_MESSAGE_LENGTH = 100 * 1024 * 1024

_CHANNEL_OPTIONS = (
("grpc.max_receive_message_length", _MAX_RECEIVE_MESSAGE_LENGTH),
("grpc.max_send_message_length", _MAX_SEND_MESSAGE_LENGTH),
)


@CrossSync.convert_class(sync_name="_AcceleratorClient")
class _AsyncAcceleratorClient:
Expand All @@ -59,7 +78,9 @@ class _AsyncAcceleratorClient:
def __init__(self, uds_path: str, auth_secret: str):
self._uds_path = uds_path
self._metadata = (("x-accelerator-token", auth_secret),)
self._channel = insecure_channel(_uds_target(uds_path))
self._channel = insecure_channel(
_uds_target(uds_path), options=_CHANNEL_OPTIONS
Comment thread
mutianf marked this conversation as resolved.
)
self._mutate_row_stub = self._channel.unary_unary(
_MUTATE_ROW_METHOD,
request_serializer=MutateRowRequest.serialize,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@

_MUTATE_ROW_METHOD = "/google.bigtable.v2.Bigtable/MutateRow"
_READ_ROWS_METHOD = "/google.bigtable.v2.Bigtable/ReadRows"
_MAX_RECEIVE_MESSAGE_LENGTH = 256 * 1024 * 1024
_MAX_SEND_MESSAGE_LENGTH = 100 * 1024 * 1024
_CHANNEL_OPTIONS = (
("grpc.max_receive_message_length", _MAX_RECEIVE_MESSAGE_LENGTH),
("grpc.max_send_message_length", _MAX_SEND_MESSAGE_LENGTH),
)


class _AcceleratorClient:
Expand All @@ -55,7 +61,9 @@ class _AcceleratorClient:
def __init__(self, uds_path: str, auth_secret: str):
self._uds_path = uds_path
self._metadata = (("x-accelerator-token", auth_secret),)
self._channel = insecure_channel(_uds_target(uds_path))
self._channel = insecure_channel(
_uds_target(uds_path), options=_CHANNEL_OPTIONS
)
self._mutate_row_stub = self._channel.unary_unary(
_MUTATE_ROW_METHOD,
request_serializer=MutateRowRequest.serialize,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Unit tests for the UDS channel configuration in _accelerator_client.py."""

from unittest import mock

import pytest

from google.cloud.bigtable.data._async import _accelerator_client as async_module
from google.cloud.bigtable.data._sync_autogen import (
_accelerator_client as sync_module,
)

_MODULES = pytest.mark.parametrize(
"module, client_name",
[
(async_module, "_AsyncAcceleratorClient"),
(sync_module, "_AcceleratorClient"),
],
ids=["async", "sync"],
)


class TestChannelOptions:
"""The caps are load-bearing, and the option keys are easy to typo.

A misspelled key is silently ignored by gRPC, which leaves the channel on
the 4 MiB default and fails only on a large row.
"""

@_MODULES
def test_options_cap_each_direction_at_the_service_limit(self, module, client_name):
# Inbound 256 MiB: the hard limit on all values in a single row.
# Outbound 100 MB: the request size the service accepts.
assert module._MAX_RECEIVE_MESSAGE_LENGTH == 256 * 1024 * 1024
assert module._MAX_SEND_MESSAGE_LENGTH == 100 * 1024 * 1024
assert module._CHANNEL_OPTIONS == (
("grpc.max_receive_message_length", 256 * 1024 * 1024),
("grpc.max_send_message_length", 100 * 1024 * 1024),
)

@_MODULES
def test_channel_is_built_with_the_options(self, module, client_name):
with mock.patch.object(module, "insecure_channel") as channel_mock:
getattr(module, client_name)("/tmp/sock", "secret")
channel_mock.assert_called_once_with(
module._uds_target("/tmp/sock"), options=module._CHANNEL_OPTIONS
)
Loading