Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,19 @@ public InstanceAdminClient getInstanceAdminClient() {
public DatabaseClient getDatabaseClient(DatabaseId db) {
synchronized (this) {
checkClosed();
SpannerOptions opts = getOptions();
String metricsProject = opts.resolveMetricsProjectId();
if (opts.isEnableBuiltInMetrics()
&& metricsProject != null
&& !metricsProject.equals(db.getInstanceId().getProject())) {
logger.log(
Level.WARNING,
"DatabaseId project ''{0}'' differs from the project used for client-side metrics"
+ " export ''{1}''. Metrics will be exported to ''{1}'', which may cause"
+ " permission errors. Set SpannerOptions.Builder.setMetricsProjectId(\"{0}\")"
+ " to fix this.",
new Object[] {db.getInstanceId().getProject(), metricsProject});
}
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.

medium

The warning for project ID mismatch is currently logged on every call to getDatabaseClient. Since this method is frequently called during normal operation, this can lead to excessive log spam and unnecessary performance overhead. It is recommended to move this check inside the conditional block where a new DatabaseClient is actually created (i.e., when !dbClients.containsKey(db)). This ensures the warning is logged at most once per DatabaseId for the lifetime of the Spanner instance.

String clientId = null;
if (dbClients.containsKey(db) && !dbClients.get(db).isValid()) {
// Close the invalidated client and remove it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ static GcpChannelPoolOptions mergeWithDefaultChannelPoolOptions(
private final boolean enableExtendedTracing;
private final boolean enableEndToEndTracing;
private final String monitoringHost;
private final String metricsProjectId;
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.

medium

When adding a new configuration field like metricsProjectId to SpannerOptions, it is essential to update the equals() and hashCode() methods to include it. Failure to do so can cause issues when SpannerOptions instances are used as keys in caches or when comparing configurations for equality.

private final TransactionOptions defaultTransactionOptions;
private final RequestOptions.ClientContext clientContext;

Expand Down Expand Up @@ -991,6 +992,7 @@ protected SpannerOptions(Builder builder) {
}
enableEndToEndTracing = builder.enableEndToEndTracing;
monitoringHost = builder.monitoringHost;
metricsProjectId = builder.metricsProjectId;
defaultTransactionOptions = builder.defaultTransactionOptions;
clientContext = builder.clientContext;
}
Expand Down Expand Up @@ -1251,6 +1253,7 @@ public static class Builder
private boolean enableBuiltInMetrics = SpannerOptions.environment.isEnableBuiltInMetrics();
private boolean enableLocationApi = SpannerOptions.environment.isEnableLocationApi();
private String monitoringHost = SpannerOptions.environment.getMonitoringHost();
private String metricsProjectId;
private SslContext mTLSContext = null;
private String experimentalHost = null;
private boolean usePlainText = false;
Expand Down Expand Up @@ -1360,6 +1363,7 @@ protected Builder() {
this.enableLocationApi = options.enableLocationApi;
this.enableEndToEndTracing = options.enableEndToEndTracing;
this.monitoringHost = options.monitoringHost;
this.metricsProjectId = options.metricsProjectId;
this.defaultTransactionOptions = options.defaultTransactionOptions;
this.clientContext = options.clientContext;
}
Expand Down Expand Up @@ -2033,6 +2037,17 @@ public Builder setMonitoringHost(String monitoringHost) {
return this;
}

/**
* Sets the GCP project ID for exporting client-side built-in metrics. Defaults to {@link
* SpannerOptions#getProjectId()} when not set. On GKE with shared VPC, the default project may
* resolve to the host project instead of the application project; set this explicitly to avoid
* {@code monitoring.metricWriter} permission errors.
*/
public Builder setMetricsProjectId(String metricsProjectId) {
this.metricsProjectId = metricsProjectId;
return this;
}

/**
* Sets whether to enable extended OpenTelemetry tracing. Enabling this option will add the
* following additional attributes to the traces that are generated by the client:
Expand Down Expand Up @@ -2443,14 +2458,14 @@ public ApiTracerFactory getApiTracerFactory() {
@InternalApi
public OpenTelemetry getBuiltInOpenTelemetry() {
return this.builtInMetricsProvider.getOrCreateOpenTelemetry(
this.getProjectId(), getCredentials(), this.monitoringHost, getUniverseDomain());
this.resolveMetricsProjectId(), getCredentials(), this.monitoringHost, getUniverseDomain());
}

public void enablegRPCMetrics(InstantiatingGrpcChannelProvider.Builder channelProviderBuilder) {
if (isEnableBuiltInMetrics() && SpannerOptions.environment.isEnableGRPCBuiltInMetrics()) {
this.builtInMetricsProvider.enableGrpcMetrics(
channelProviderBuilder,
this.getProjectId(),
this.resolveMetricsProjectId(),
getCredentials(),
this.monitoringHost,
getUniverseDomain());
Expand Down Expand Up @@ -2499,7 +2514,7 @@ private ApiTracerFactory getDefaultApiTracerFactory() {
private ApiTracerFactory createMetricsApiTracerFactory() {
OpenTelemetry openTelemetry =
this.builtInMetricsProvider.getOrCreateOpenTelemetry(
this.getProjectId(), getCredentials(), this.monitoringHost, getUniverseDomain());
this.resolveMetricsProjectId(), getCredentials(), this.monitoringHost, getUniverseDomain());

return openTelemetry != null
? new BuiltInMetricsTracerFactory(
Expand Down Expand Up @@ -2543,6 +2558,19 @@ String getMonitoringHost() {
return monitoringHost;
}

/**
* Returns the GCP project ID for client-side metrics export. Returns the explicit value if set
* via {@link Builder#setMetricsProjectId}, otherwise falls back to {@link #getProjectId()}.
*/
String resolveMetricsProjectId() {
return metricsProjectId != null ? metricsProjectId : getProjectId();
}

/** Returns the explicitly configured metrics project ID, or null if not set. */
public String getMetricsProjectId() {
return metricsProjectId;
}

public TransactionOptions getDefaultTransactionOptions() {
return defaultTransactionOptions;
}
Expand Down
Loading