Skip to content

feat(bigtable): route read_row/mutate_row through the accelerator with native fallback - #2

Open
mutianf wants to merge 5 commits into
accel-01-daemon-subprocess-wrapperfrom
accel-02-route-read-mutate
Open

feat(bigtable): route read_row/mutate_row through the accelerator with native fallback#2
mutianf wants to merge 5 commits into
accel-01-daemon-subprocess-wrapperfrom
accel-02-route-read-mutate

Conversation

@mutianf

@mutianf mutianf commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Stack PR 2/7 — base: accel-01-daemon-subprocess-wrapper

Routes read_row and mutate_row through the accelerator daemon when eligible, via the method-name gate in _accelerator/_routing.py and per-method dispatch branches.


Accelerator stack

  1. daemon subprocess wrapper
  2. route read_row and mutate_row through daemon
  3. forward auth/identity config to daemon
  4. verify daemon identity before routing
  5. authenticate RPCs with stdin handshake secret
  6. pass Python client version for User-Agent
  7. package prebuilt daemon into wheel

Comment thread packages/google-cloud-bigtable/google/cloud/bigtable/data/_async/client.py Outdated
Comment thread packages/google-cloud-bigtable/google/cloud/bigtable/data/_async/client.py Outdated
@mutianf
mutianf force-pushed the accel-02-route-read-mutate branch from 934f2cd to b86802c Compare August 10, 2026 16:38
Comment thread packages/google-cloud-bigtable/google/cloud/bigtable/data/_async/client.py Outdated
@mutianf
mutianf force-pushed the accel-02-route-read-mutate branch 2 times, most recently from 8543853 to 86edefc Compare August 11, 2026 14:07
@mutianf mutianf changed the title feat(bigtable): route read_row and mutate_row through accelerator daemon feat(bigtable): route read_row/mutate_row through the accelerator with native fallback Aug 11, 2026
@mutianf
mutianf force-pushed the accel-02-route-read-mutate branch 2 times, most recently from 1fc9db4 to 6d4ace5 Compare August 11, 2026 21:47

@daniel-sanche daniel-sanche left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm just starting to get my head around this, but let some comments and questions from my first pass



def _grpc_code(exc: BaseException) -> StatusCode | None:
"""Best-effort extraction of a gRPC status code from an exception."""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This would work for grpc.RpcError exceptions. But there's also api_core.exceptions.GoogleAPICallError errors, which store this as grpc_status_code.

I don't know if that'll be relevant here

(We have similar code here. Maybe we should make this a shared helper?)


Never escapes the Table method that raises it: the method catches it and
falls through to the native code path.
"""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This could also live in google.cloud.bigtable.data.exceptions, depending on the usage

f"to the native client: {exc}",
RuntimeWarning,
stacklevel=2,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do we want to prevent this fall-back if explicit is set?

metric=ActiveOperationMetric(OperationType.READ_ROWS, is_streaming=False),
retryable_exceptions=(),
)
row_merger._operation_metric.start_attempt()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do we still need to collect the metrics in Python? Or does the accelerator handle that too?

I might need to double-check that read_rows still works as expected without the tracked_retry wrapper, unless you already verified that

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Good point, I think this line should be removed.

row_merger.request, timeout=operation_timeout
)
chunked_stream = row_merger.chunk_stream(stream)
results = [a async for a in row_merger.merge_rows(chunked_stream)]

@daniel-sanche daniel-sanche Aug 21, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is read_rows_stream out of scope for now? That's the main part that I'd be worried about, since we have to be careful about how generators are wrapped/errors are propagated

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Yeah, it's out of scope in this iteration.

)
except _AcceleratorFallback:
# Daemon can't serve this call; fall through to the native path.
pass

@daniel-sanche daniel-sanche Aug 21, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I wonder if there's a solution here using decorators?

I like the idea of being able to mark each method with @accelerator_route('mutate_rows_via_accelerator'), and then have generic wrapper logic that handles the routing/fallback, with the native python implementation left mostly unchanged. That feels like it could give us some nice abstractions

But maybe that would introduce too many complications

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This feels like a bigger change, maybe i'll do it in a future refactor.

return [row async for row in row_generator]

@CrossSync.convert
async def _read_row_via_accelerator(

@daniel-sanche daniel-sanche Aug 21, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Could it make sense to move this logic into accelerator_client.read_rows, to keep the accelerator implementation in one place?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

accelerator implements bigtable.proto which doesn't have a read_row rpc. This already routes the request to accelerator_client.read_rows?

…h native fallback

Change-Id: I6fc1d2563ca3185b12efb87b96eff2b7be438405
…back comments

Change-Id: I191c52745e0cd10e5ab99e5e06135cd2e518bb9f
Change-Id: I72d5e456cdf722498ace021cc1f976cc1469a4dc
…NIMPLEMENTED

Change-Id: Ifad0d9eb9d7b4d03fff07de2549a6d3294cf2f16
…lback

Change-Id: I1b7382f6bfd8e70e7c6035e4b8ebb93ee3e282f3
@mutianf
mutianf force-pushed the accel-02-route-read-mutate branch from 1b2b842 to 4419ba3 Compare August 24, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants