Skip to content

Commit a69e2cb

Browse files
fix(bigtable): standardize client side metrics (#17899)
Made some adjustments to the client side metrics system after comparing against node and java implemenations (with gemini) The diffs for each commit can be looked it in isolation 1 ([64acfa1)](64acfa1): throttling_latencies is now recorded once per operation - java records grpc time as part of throttling latencies. Python just tracks flow-control time - flow control happens once at the start of an operation: once the mutations have made it past the flow control gate, retries don't have to wait again, they have space reserved until they reach a terminal state - Python was previously recording throttling_latencieson each attempt, but recording the same per-operation value on each attempt. This would give misleading data - Now, we only record it once per operation 2 [(c2c8daa)](c2c8daa): removing backoff time from application blocking latencies - previously, python was including time spent backing off between attempts as part of the application_latencies metric - other languages do not include this, so we can drop it from Python as well
1 parent 62ff6f3 commit a69e2cb

3 files changed

Lines changed: 32 additions & 28 deletions

File tree

packages/google-cloud-bigtable/google/cloud/bigtable/data/_metrics/data_model.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ class ActiveAttemptMetric:
140140
# time waiting on user to process the response, in nanoseconds
141141
# currently only relevant for ReadRows
142142
application_blocking_time_ns: int = 0
143-
# backoff time is added to application_blocking_time_ns
144143
backoff_before_attempt_ns: int = 0
145144

146145

packages/google-cloud-bigtable/google/cloud/bigtable/data/_metrics/handlers/opentelemetry.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ def on_operation_complete(self, op: CompletedOperationMetric) -> None:
181181
- operation_latencies
182182
- retry_count
183183
- first_response_latencies
184+
- throttling_latencies
184185
"""
185186
labels = {
186187
"method": op.op_type.value,
@@ -204,6 +205,13 @@ def on_operation_complete(self, op: CompletedOperationMetric) -> None:
204205
# only record completed attempts if there were retries
205206
if op.completed_attempts:
206207
self.otel.retry_count.add(len(op.completed_attempts) - 1, labels)
208+
if (
209+
op.op_type == OperationType.BULK_MUTATE_ROWS
210+
and op.flow_throttling_time_ns > 0
211+
):
212+
self.otel.throttling_latencies.record(
213+
op.flow_throttling_time_ns / NS_TO_MS, labels
214+
)
207215

208216
def on_attempt_complete(
209217
self, attempt: CompletedAttemptMetric, op: ActiveOperationMetric
@@ -214,7 +222,6 @@ def on_attempt_complete(
214222
- server_latencies
215223
- connectivity_error_count
216224
- application_latencies
217-
- throttling_latencies
218225
"""
219226
labels = {
220227
"method": op.op_type.value,
@@ -229,13 +236,8 @@ def on_attempt_complete(
229236
attempt.duration_ns / NS_TO_MS,
230237
{"streaming": is_streaming, "status": status, **labels},
231238
)
232-
flow_throttling = (
233-
op.flow_throttling_time_ns / NS_TO_MS if op.flow_throttling_time_ns else 0
234-
)
235-
self.otel.throttling_latencies.record(flow_throttling, labels)
236239
self.otel.application_latencies.record(
237-
(attempt.application_blocking_time_ns + attempt.backoff_before_attempt_ns)
238-
/ NS_TO_MS,
240+
attempt.application_blocking_time_ns / NS_TO_MS,
239241
labels,
240242
)
241243
if attempt.gfe_latency_ns is not None:

packages/google-cloud-bigtable/tests/unit/data/_metrics/test_opentelemetry_handler.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -323,31 +323,35 @@ def test_on_attempt_complete_attempt_latencies(self):
323323
)
324324

325325
@pytest.mark.parametrize(
326-
"is_first_attempt,flow_throttling_ns",
327-
[(True, 54321), (False, 0), (True, 0)],
326+
"op_type,flow_throttling_ns,should_record",
327+
[
328+
(OperationType.BULK_MUTATE_ROWS, 54321, True),
329+
(OperationType.BULK_MUTATE_ROWS, 0, False),
330+
(OperationType.READ_ROWS, 54321, False),
331+
],
328332
)
329-
def test_on_attempt_complete_throttling_latencies(
330-
self, is_first_attempt, flow_throttling_ns
333+
def test_on_operation_complete_throttling_latencies(
334+
self, op_type, flow_throttling_ns, should_record
331335
):
332336
mock_instruments = mock.Mock(throttling_latencies=mock.Mock())
333337
handler = self._make_one(instruments=mock_instruments)
334-
attempt = CompletedAttemptMetric(
338+
op = CompletedOperationMetric(
339+
op_type=op_type,
335340
duration_ns=1234567,
336-
end_status=StatusCode.OK,
337-
)
338-
op = ActiveOperationMetric(
339-
op_type=OperationType.READ_ROWS,
341+
completed_attempts=[],
342+
final_status=StatusCode.OK,
343+
cluster_id="cluster",
344+
zone="zone",
345+
is_streaming=False,
340346
flow_throttling_time_ns=flow_throttling_ns,
341347
)
342-
if not is_first_attempt:
343-
op.completed_attempts.append(mock.Mock())
344-
handler.on_attempt_complete(attempt, op)
345-
expected_throttling = 0
346-
if is_first_attempt:
347-
expected_throttling += flow_throttling_ns / 1e6
348-
mock_instruments.throttling_latencies.record.assert_called_once_with(
349-
pytest.approx(expected_throttling), mock.ANY
350-
)
348+
handler.on_operation_complete(op)
349+
if should_record:
350+
mock_instruments.throttling_latencies.record.assert_called_once_with(
351+
pytest.approx(flow_throttling_ns / 1e6), mock.ANY
352+
)
353+
else:
354+
mock_instruments.throttling_latencies.record.assert_not_called()
351355

352356
def test_on_attempt_complete_application_latencies(self):
353357
mock_instruments = mock.Mock(application_latencies=mock.Mock())
@@ -361,8 +365,7 @@ def test_on_attempt_complete_application_latencies(self):
361365
op = ActiveOperationMetric(op_type=OperationType.READ_ROWS)
362366
handler.on_attempt_complete(attempt, op)
363367
mock_instruments.application_latencies.record.assert_called_once_with(
364-
(attempt.application_blocking_time_ns + attempt.backoff_before_attempt_ns)
365-
/ 1e6,
368+
attempt.application_blocking_time_ns / 1e6,
366369
mock.ANY,
367370
)
368371

0 commit comments

Comments
 (0)