Skip to content

fix(bigtable): data client should acknowledge all mutations in batch - #18124

Open
mutianf wants to merge 1 commit into
googleapis:mainfrom
mutianf:bigtable-v3-mutate-rows-response-count
Open

fix(bigtable): data client should acknowledge all mutations in batch#18124
mutianf wants to merge 1 commit into
googleapis:mainfrom
mutianf:bigtable-v3-mutate-rows-response-count

Conversation

@mutianf

@mutianf mutianf commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

fail V3 mutate_rows entries the server never acknowledged

…dged

Change-Id: I9a166818c5829b446553fc1427d1f1d1ec078d0d
@mutianf
mutianf requested a review from a team as a code owner August 14, 2026 21:51

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a response completeness check to both the async and sync implementations of _mutate_rows.py, ensuring that any mutation entries not acknowledged by the server are explicitly failed with a ClientError rather than being silently treated as successful. While the added test coverage is thorough, the reviewer correctly identified a critical bug in both implementations: when mutations are retried across multiple attempts, obsolete acknowledgments and errors from previous attempts are not cleared, which prevents the completeness check from correctly identifying silently dropped entries in subsequent attempts. To resolve this, the state of retried entries should be cleared at the start of each attempt.

Comment on lines +150 to +154
if len(self._acknowledged_indices) != len(self.mutations):
for idx in range(len(self.mutations)):
if idx not in self._acknowledged_indices and (
idx not in self.errors
):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Bug: Unacknowledged entries in retried attempts will not be correctly failed with ClientError

There is a subtle bug here when mutations are retried across multiple attempts:

  1. Scenario:

    • A mutation entry at index i fails with a retryable error (e.g., UNAVAILABLE) in Attempt 1.
    • This adds i to self._acknowledged_indices and the retryable error to self.errors[i].
    • Since it's retryable, it is retried in Attempt 2.
    • In Attempt 2, the stream finishes successfully but silently drops/ignores index i (i.e., no response entry is returned for it, and no stream-level exception is raised).
  2. The Issue:

    • In the finally block, len(self._acknowledged_indices) != len(self.mutations) is checked. Since i was acknowledged in Attempt 1, it is already in self._acknowledged_indices. If all other entries were acknowledged, this check will evaluate to False and the completeness check won't run.
    • Even if it runs, idx not in self._acknowledged_indices will be False and idx not in self.errors will be False (due to the error from Attempt 1).
    • Thus, the entry will not be marked with ClientError. Instead, the operation will fail with the obsolete retryable error from Attempt 1, which might cause the client to incorrectly retry the entire batch.
  3. Recommended Solution:
    At the start of each attempt (e.g., at the beginning of _run_attempt), we should clear the state of the entries being retried so that we only consider acknowledgments and errors from the current attempt:

    self._acknowledged_indices.difference_update(self.remaining_indices)
    for idx in self.remaining_indices:
        self.errors.pop(idx, None)

Comment on lines +116 to +121
if len(self._acknowledged_indices) != len(self.mutations):
for idx in range(len(self.mutations)):
if (
idx not in self._acknowledged_indices
and idx not in self.errors
):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Bug: Unacknowledged entries in retried attempts will not be correctly failed with ClientError

This is the same issue as identified in the async version (_async/_mutate_rows.py). When mutations are retried across multiple attempts, obsolete acknowledgments and errors from previous attempts are not cleared, preventing the completeness check from correctly identifying and failing silently dropped entries in the final attempt.

Recommended Solution:
At the start of each attempt (e.g., at the beginning of _run_attempt), clear the state of the entries being retried:

self._acknowledged_indices.difference_update(self.remaining_indices)
for idx in self.remaining_indices:
    self.errors.pop(idx, None)

@daniel-sanche daniel-sanche changed the title feat(bigtable): fail V3 mutate_rows entries the server never acknowle… fix(bigtable): data client should acknowledge all mutations in batch Aug 14, 2026
@daniel-sanche

Copy link
Copy Markdown
Contributor

Did your original approach not work? I thought that seemed like a good solution.

Gemini pointed out some issues with the new approach. It could be difficult to keep a global _acknowledged_indices map up-to-date, because each attempt is another chance to drop a mutation. It would be better if we could keep all the per-attempt state within _run_attempt

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