Skip to content

fix: Correct project id on client side metrics by avoiding getProjectId calls with the metric service client#1757

Merged
gcf-merge-on-green[bot] merged 20 commits intomainfrom
467064670-pass-project-id-through-otel
Jan 14, 2026
Merged

fix: Correct project id on client side metrics by avoiding getProjectId calls with the metric service client#1757
gcf-merge-on-green[bot] merged 20 commits intomainfrom
467064670-pass-project-id-through-otel

Conversation

@danieljbruce
Copy link
Contributor

Description

The project id being recorded for client side metrics is incorrect. The client side metrics monitoring tests are showing that the project id the metrics get recorded for does not actually match the project id the client makes a grpc call for. Specifically, values recorded for bigtable.googleapis.com/frontend_server/handler_latencies don't match values recorded for bigtable.googleapis.com/internal/client/attempt_latencies, but they should match.

There is some speculation that since getProjectId calls on the MetricServiceClient determine the projectId recorded for client side metrics that the incorrect projectId is due to the differences between this getProjectId call on the MetricServiceClient and the way the project id is fetched on the Bigtable client. Therefore, we introduce this PR to record a projectId that is certain to be an exact match of the project id on the Bigtable client.

Impact

This has a high probability of correcting the project id for the client side metrics

Testing

The fixtures have changed to accommodate for the fact that we now pass the projectId through the OTEL stack.

@danieljbruce danieljbruce requested a review from a team January 13, 2026 16:07
@danieljbruce danieljbruce requested a review from a team as a code owner January 13, 2026 16:07
@product-auto-label product-auto-label bot added size: m Pull request size is medium. api: bigtable Issues related to the googleapis/nodejs-bigtable API. labels Jan 13, 2026
@danieljbruce danieljbruce requested a review from mutianf January 13, 2026 16:21
@danieljbruce danieljbruce added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jan 13, 2026
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jan 13, 2026
client_name: string;
streaming: StreamingState;
status: string;
projectId: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think projectId should be part of IMetricsCollectorData

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made this change

* @param {grpc.status} attemptStatus The grpc status for the attempt.
*/
onAttemptComplete(attemptStatus: grpc.status) {
onAttemptComplete(projectId: string, attemptStatus: grpc.status) {
Copy link
Contributor

Choose a reason for hiding this comment

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

projectId is static and won't change per request. Why not add it to line 228 this.getMetricsCollectorData()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. Then we can get it from this.tabularApiSurface.

* @param exporter The exporter the metrics will be sent to.
*/
function createInstruments(exporter: PushMetricExporter): MetricsInstruments {
function createInstruments(
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you need to update this, instead you can get it from the attirbutes in the data point:
https://github.com/googleapis/nodejs-bigtable/blob/3e532ab657409c7acd09008c143c41d36d9efd41/src/client-side-metrics/exporter.ts#L134C15-L135, similar to how you get instance id.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can definitely do it that way, but then what do we do here? (

name: `projects/${projectId}`,
)

Should we just use the projectId from the first data point for name?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think you need to update this, instead you can get it from the attirbutes in the data point

There is nothing wrong with doing it this way and I'll do this change, but is there any reason it is preferred?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Second thought, I don't think we should do it this way.

instead you can get it from the attributes in the data point

This is only true if I start sending the projectId through the OTEL stack which I am not doing right now. And I don't recommend doing that because it increases the data load on the OTEL stack with a bunch of projectId values across all the metrics that we know are all the same.

Let me know what you think.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we just use the projectId from the first data point for name?

Yes.

but is there any reason it is preferred?

OTEL attributes provide context of a metrics. If someone attaches a different exporter on their OTEL instance, without the project id as part of the attributes, this context would be missing and they won't be able to group or filter metrics on project id. It also feels weird to have the project id on Resource because Resource describes where the metric is coming from (in this case it's just bigtable). The extra memory usage from an extra project id field is minimal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made the change you requested.


// Helper to create interceptor provider for OperationMetricsCollector
function createMetricsInterceptorProvider(
table: TabularApiSurface,
Copy link
Contributor

Choose a reason for hiding this comment

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

why do we need to pass this in?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't. I removed this. We needed it before because onAttemptComplete needed the projectId, but now it doesn't need the project id.

* @param exporter The exporter the metrics will be sent to.
*/
function createInstruments(exporter: PushMetricExporter): MetricsInstruments {
function createInstruments(
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we just use the projectId from the first data point for name?

Yes.

but is there any reason it is preferred?

OTEL attributes provide context of a metrics. If someone attaches a different exporter on their OTEL instance, without the project id as part of the attributes, this context would be missing and they won't be able to group or filter metrics on project id. It also feels weird to have the project id on Resource because Resource describes where the metric is coming from (in this case it's just bigtable). The extra memory usage from an extra project id field is minimal.

@danieljbruce danieljbruce requested a review from mutianf January 14, 2026 17:56
table: data.metricsCollectorData.table,
cluster: data.metricsCollectorData.cluster,
zone: data.metricsCollectorData.zone,
projectId: data.metricsCollectorData.projectId,
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can we move this above instanceId?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

table: data.metricsCollectorData.table,
cluster: data.metricsCollectorData.cluster,
zone: data.metricsCollectorData.zone,
projectId: data.metricsCollectorData.projectId,
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

zone?: string;
app_profile?: string;
method: MethodName;
projectId: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can we move this on top of instance id?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@danieljbruce danieljbruce added the automerge Merge the pull request once unit tests and other checks pass. label Jan 14, 2026
@gcf-merge-on-green gcf-merge-on-green bot merged commit a719d9f into main Jan 14, 2026
24 checks passed
@gcf-merge-on-green gcf-merge-on-green bot removed the automerge Merge the pull request once unit tests and other checks pass. label Jan 14, 2026
@gcf-merge-on-green gcf-merge-on-green bot deleted the 467064670-pass-project-id-through-otel branch January 14, 2026 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: bigtable Issues related to the googleapis/nodejs-bigtable API. size: m Pull request size is medium.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants