feat(bigtable): client side metrics handlers - #16760
Conversation
…_2_instrumentation_advanced
There was a problem hiding this comment.
Code Review
This pull request implements client-side metrics for the Bigtable library using OpenTelemetry, including a custom exporter for Google Cloud Monitoring. The review feedback focuses on improving resource efficiency by moving the MeterProvider and client_uid generation to the client level to avoid thread leaks and inconsistent identifiers across tables. Additionally, recommendations were made to handle potential KeyError exceptions in the exporter, improve logging for background export failures, and ensure non-negative timeouts during batch writes.
| write_ind = 0 | ||
| while write_ind < len(series): | ||
| # find time left for next batch | ||
| timeout = deadline - time.time() if deadline else gapic_v1.method.DEFAULT |
There was a problem hiding this comment.
why do we need to calculate the remaining deadline for each batch? I think each batch should just use the default timeout.
There was a problem hiding this comment.
The intention here is to be consistent with the OpenTelemetry exporter spec, which passes in a timeout value for exporters to conform to.
I'm not entirely sure what happens if we ignore it, but I can look into it if needed. Does Java not have anything similar?
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces client-side metrics collection and exporting to Google Cloud Monitoring for the Bigtable Python client by adding GoogleCloudMetricsHandler and BigtableMetricsExporter. Feedback on the changes highlights several critical issues, including potential KeyError and TypeError exceptions during metric label processing and protobuf serialization, broad exception handling that silently masks export failures, and side-effects from instantiating OpenTelemetry instruments as default arguments. Additionally, corrections are needed in the system tests to resolve mismatches in metric names and type prefixes.
…rics/handlers/opentelemetry.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…rics/handlers/gcp_exporter.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
| self.meter_provider = MeterProvider( | ||
| metric_readers=[gcp_reader], views=VIEW_LIST | ||
| ) | ||
| otel = _OpenTelemetryInstruments(meter_provider=self.meter_provider) |
There was a problem hiding this comment.
Looks like this should support application has it's own otel instrumentation. Is there an example of how that can be done somewhere?
There was a problem hiding this comment.
Yes, I designed the architecture to work well with user-provided otel meter providers, but so far I haven't actually exposed it. I thought we should wait for an ask before making it an official feature
If you want me to add this now, I can. But it might be better as a separate PR
| super().__init__() | ||
| self.client = MetricServiceClient(*client_args, **client_kwargs) | ||
| self.prefix = "bigtable.googleapis.com/internal/client" | ||
| self.project_id = project_id |
There was a problem hiding this comment.
I don't think CloudMonitoring client needs to be created with a project id. The project id can be extracted from metrics label ?
There was a problem hiding this comment.
Yeah, I think I originally did it this way
- because we know the expected project at client init-time
- to be more in line with the upstream OTel Exporter
But I don't think there's any reason we need to do it this way. I changed it to extract all project ids from the labels
| write_ind += max_batch_size | ||
|
|
||
| @staticmethod | ||
| def _to_point( |
There was a problem hiding this comment.
are the resouce level attributes (project, instance, table, cluster, zone) removed from the metric data points?
There was a problem hiding this comment.
Yes, that was happening here. But I updated this code to be a bit cleaner in this area
| metric_kind = MetricDescriptor.MetricKind.CUMULATIVE | ||
| all_series: list[TimeSeries] = [] | ||
| # process each metric from OTel format into Cloud Monitoring format | ||
| for resource_metric in metrics_data.resource_metrics: |
There was a problem hiding this comment.
I assume one of these for loops is filtering out bigtable only metrics?
There was a problem hiding this comment.
For this handler, we're using an isolated MeterProvider/instruments for bigtable metrics, so we don't actually expect to see any non-bigtable metrics come through.
(we made it so users can also interact with these metrics in their own OTel set-up if we want to expose that, but this pipeline is private)
There was a problem hiding this comment.
But also, yes, there is some filtering here, skipping over data that doesn't have the project id set properly
| # fixed labels sent with each metric update | ||
| self.shared_labels = { | ||
| "client_name": f"python-bigtable/{client_version}", | ||
| "client_uid": client_uid or self._generate_client_uid(), |
There was a problem hiding this comment.
how is client_uid generated otherwise? maybe we should always generate it? This is really to prevent cloud monitoring to having issue merging the streams. maybe we should move it to the exporter?
There was a problem hiding this comment.
how is client_uid generated otherwise? maybe we should always generate it?
Currently, it's always auto-generated, unless explicitly passed in. I don't think we currently need to hard-code it anywhere (outside of possible testing), but if we choose to expose this to end-users, they may want to be able to control this field for their private metric systems
maybe we should move it to the exporter?
This is where we're setting all the other labels, so it feels like a natural place for it. But yeah, if you want to expose these metrics to external OTel set-ups, and don't want them to see this field, we can move it to the GCP-specific subclass instead
Migrate googleapis/python-bigtable#1189 to the monorepo
This PR builds off of googleapis/python-bigtable#1187 to add handlers to the client-side metrics system, which can subscribe to the metrics stream, and export the results into different collection systems
We add two handlers to the system:
GoogleCloudMetricsHandler: sends metrics to a private OpenTelemetry meter, and then periodically exports them to GCP. Built on top ofOpenTelemetryMetricsHandlerOpenTelemetryMetricsHandler: sends metrics to the root MeterProvider, so the user can access the exported metrics for their own systems. This will be off by default, but can be added alongsideGoogleCloudMetricsHandlerif needed