Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit d5e012d

Browse files
committed
strip operation id from metadata before request
2 parents bd9ab70 + 73f4b3c commit d5e012d

2 files changed

Lines changed: 51 additions & 22 deletions

File tree

google/cloud/bigtable/data/_async/metrics_interceptor.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,21 @@ def _with_operation_from_metadata(func):
4343

4444
@wraps(func)
4545
def wrapper(self, continuation, client_call_details, request):
46-
key = next(
47-
(
48-
m[1]
49-
for m in client_call_details.metadata
50-
if m[0] == OPERATION_INTERCEPTOR_METADATA_KEY
51-
),
52-
None,
53-
)
54-
operation: "ActiveOperationMetric" = self.operation_map.get(key)
46+
found_operation_id: str | None = None
47+
new_metadata = client_call_details.metadata
48+
if client_call_details.metadata:
49+
# find operation key from metadata
50+
temp_metadata = []
51+
for k, v in client_call_details.metadata:
52+
if k == OPERATION_INTERCEPTOR_METADATA_KEY:
53+
found_operation_id = v
54+
else:
55+
temp_metadata.append((k, v))
56+
new_metadata = temp_metadata
57+
# update client_call_details to drop the operation key metadata
58+
client_call_details.metadata = new_metadata
59+
60+
operation: "ActiveOperationMetric" = self.operation_map.get(found_operation_id)
5561
if operation:
5662
# start a new attempt if not started
5763
if (

tests/unit/data/_async/test_metrics_interceptor.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import pytest
1616
from grpc import RpcError
17+
from grpc import ClientCallDetails
1718

1819
from google.cloud.bigtable.data._metrics.data_model import OperationState
1920
from google.cloud.bigtable.data._cross_sync import CrossSync
@@ -128,12 +129,34 @@ def test_on_operation_cancelled(self):
128129
op.cancel()
129130
assert op.uuid not in instance.operation_map
130131

132+
@CrossSync.pytest
133+
async def test_strip_operation_id_metadata(self):
134+
"""
135+
After operation id is detected in metadata, the field should be stripped out before calling continuation
136+
"""
137+
from google.cloud.bigtable.data._metrics.data_model import (
138+
OPERATION_INTERCEPTOR_METADATA_KEY,
139+
)
140+
141+
instance = self._make_one()
142+
op = mock.Mock()
143+
op.uuid = "test-uuid"
144+
op.state = OperationState.ACTIVE_ATTEMPT
145+
instance.operation_map[op.uuid] = op
146+
continuation = CrossSync.Mock()
147+
details = ClientCallDetails()
148+
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid), ("other_key", "other_value")]
149+
await instance.intercept_unary_unary(continuation, details, mock.Mock())
150+
assert details.metadata == [("other_key", "other_value")]
151+
assert continuation.call_count == 1
152+
assert continuation.call_args[0][0].metadata == [("other_key", "other_value")]
153+
131154
@CrossSync.pytest
132155
async def test_unary_unary_interceptor_op_not_found(self):
133156
"""Test that interceptor call cuntinuation if op is not found"""
134157
instance = self._make_one()
135158
continuation = CrossSync.Mock()
136-
details = mock.Mock()
159+
details = ClientCallDetails()
137160
details.metadata = []
138161
request = mock.Mock()
139162
await instance.intercept_unary_unary(continuation, details, request)
@@ -155,7 +178,7 @@ async def test_unary_unary_interceptor_success(self):
155178
call = continuation.return_value
156179
call.trailing_metadata = CrossSync.Mock(return_value=[("a", "b")])
157180
call.initial_metadata = CrossSync.Mock(return_value=[("c", "d")])
158-
details = mock.Mock()
181+
details = ClientCallDetails()
159182
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
160183
request = mock.Mock()
161184
result = await instance.intercept_unary_unary(continuation, details, request)
@@ -180,7 +203,7 @@ async def test_unary_unary_interceptor_failure(self):
180203
exc.trailing_metadata = CrossSync.Mock(return_value=[("a", "b")])
181204
exc.initial_metadata = CrossSync.Mock(return_value=[("c", "d")])
182205
continuation = CrossSync.Mock(side_effect=exc)
183-
details = mock.Mock()
206+
details = ClientCallDetails()
184207
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
185208
request = mock.Mock()
186209
with pytest.raises(RpcError) as e:
@@ -207,7 +230,7 @@ async def test_unary_unary_interceptor_failure_no_metadata(self):
207230
call = continuation.return_value
208231
call.trailing_metadata = CrossSync.Mock(return_value=[("a", "b")])
209232
call.initial_metadata = CrossSync.Mock(return_value=[("c", "d")])
210-
details = mock.Mock()
233+
details = ClientCallDetails()
211234
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
212235
request = mock.Mock()
213236
with pytest.raises(RpcError) as e:
@@ -234,7 +257,7 @@ async def test_unary_unary_interceptor_failure_generic(self):
234257
call = continuation.return_value
235258
call.trailing_metadata = CrossSync.Mock(return_value=[("a", "b")])
236259
call.initial_metadata = CrossSync.Mock(return_value=[("c", "d")])
237-
details = mock.Mock()
260+
details = ClientCallDetails()
238261
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
239262
request = mock.Mock()
240263
with pytest.raises(ValueError) as e:
@@ -249,7 +272,7 @@ async def test_unary_stream_interceptor_op_not_found(self):
249272
"""Test that interceptor calls continuation if op is not found"""
250273
instance = self._make_one()
251274
continuation = CrossSync.Mock()
252-
details = mock.Mock()
275+
details = ClientCallDetails()
253276
details.metadata = []
254277
request = mock.Mock()
255278
await instance.intercept_unary_stream(continuation, details, request)
@@ -274,7 +297,7 @@ async def test_unary_stream_interceptor_success(self):
274297
call = continuation.return_value
275298
call.trailing_metadata = CrossSync.Mock(return_value=[("a", "b")])
276299
call.initial_metadata = CrossSync.Mock(return_value=[("c", "d")])
277-
details = mock.Mock()
300+
details = ClientCallDetails()
278301
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
279302
request = mock.Mock()
280303
wrapper = await instance.intercept_unary_stream(continuation, details, request)
@@ -304,7 +327,7 @@ async def test_unary_stream_interceptor_failure_mid_stream(self):
304327
call = continuation.return_value
305328
call.trailing_metadata = CrossSync.Mock(return_value=[("a", "b")])
306329
call.initial_metadata = CrossSync.Mock(return_value=[("c", "d")])
307-
details = mock.Mock()
330+
details = ClientCallDetails()
308331
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
309332
request = mock.Mock()
310333
wrapper = await instance.intercept_unary_stream(continuation, details, request)
@@ -336,7 +359,7 @@ async def test_unary_stream_interceptor_failure_start_stream(self):
336359

337360
continuation = CrossSync.Mock()
338361
continuation.side_effect = exc
339-
details = mock.Mock()
362+
details = ClientCallDetails()
340363
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
341364
request = mock.Mock()
342365
with pytest.raises(RpcError) as e:
@@ -365,7 +388,7 @@ async def test_unary_stream_interceptor_failure_start_stream_no_metadata(self):
365388

366389
continuation = CrossSync.Mock()
367390
continuation.side_effect = exc
368-
details = mock.Mock()
391+
details = ClientCallDetails()
369392
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
370393
request = mock.Mock()
371394
with pytest.raises(RpcError) as e:
@@ -394,7 +417,7 @@ async def test_unary_stream_interceptor_failure_start_stream_generic(self):
394417

395418
continuation = CrossSync.Mock()
396419
continuation.side_effect = exc
397-
details = mock.Mock()
420+
details = ClientCallDetails()
398421
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
399422
request = mock.Mock()
400423
with pytest.raises(ValueError) as e:
@@ -424,7 +447,7 @@ async def test_unary_unary_interceptor_start_operation(self, initial_state):
424447
call = continuation.return_value
425448
call.trailing_metadata = CrossSync.Mock(return_value=[])
426449
call.initial_metadata = CrossSync.Mock(return_value=[])
427-
details = mock.Mock()
450+
details = ClientCallDetails()
428451
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
429452
request = mock.Mock()
430453
await instance.intercept_unary_unary(continuation, details, request)
@@ -450,7 +473,7 @@ async def test_unary_stream_interceptor_start_operation(self, initial_state):
450473
call = continuation.return_value
451474
call.trailing_metadata = CrossSync.Mock(return_value=[])
452475
call.initial_metadata = CrossSync.Mock(return_value=[])
453-
details = mock.Mock()
476+
details = ClientCallDetails()
454477
details.metadata = [(OPERATION_INTERCEPTOR_METADATA_KEY, op.uuid)]
455478
request = mock.Mock()
456479
await instance.intercept_unary_stream(continuation, details, request)

0 commit comments

Comments
 (0)