Skip to content

DatastoreOpenTelemetryOptions.exportBuiltinMetricsToGoogleCloudMonitoring is dead code — BuiltInDatastoreMetricsProvider always creates PeriodicMetricReader regardless of flag value #4500

Description

@raphz

Summary

DatastoreOpenTelemetryOptions exposes an exportBuiltinMetricsToGoogleCloudMonitoring flag that defaults to false. The clear intent is to allow users to opt out of the built-in Cloud Monitoring metrics export. However, BuiltInDatastoreMetricsProvider.createOpenTelemetry() never reads this flag, so the PeriodicMetricReader is unconditionally created for any application using real credentials.

The result is an unavoidable "Exporter failed" warning logged every 60 seconds by io.opentelemetry.sdk.metrics.export.PeriodicMetricReader for any application that either:

  • runs locally with ADC credentials that lack roles/monitoring.metricWriter, or
  • intentionally does not want to export Datastore metrics to Cloud Monitoring.

Affected version

google-cloud-datastore:2.41.0

Steps to reproduce

  1. Use google-cloud-datastore:2.41.0 with real ADC credentials (e.g. a service account without roles/monitoring.metricWriter)
  2. Do not set DATASTORE_EMULATOR_HOST or use NoCredentials
  3. Start the application

Observed: PeriodicMetricReader is created and logs "Exporter failed" every ~60 seconds to monitoring.googleapis.com.

Expected: Since exportBuiltinMetricsToGoogleCloudMonitoring defaults to false, no PeriodicMetricReader should be created and no export should be attempted.

Root cause

In DatastoreImpl, the constructor unconditionally calls:

BuiltInDatastoreMetricsProvider.INSTANCE.createOpenTelemetry(options)

Inside BuiltInDatastoreMetricsProvider.createOpenTelemetry(), the only conditions that return OpenTelemetry.noop() are:

  • DATASTORE_EMULATOR_HOST system property / env var is set
  • credentials are NoCredentials
  • MetricServiceClient creation throws IOException

The flag options.getOpenTelemetryOptions().isExportBuiltinMetricsToGoogleCloudMonitoring() is never checked. The fix should be straightforward:

  // BuiltInDatastoreMetricsProvider.java                                                                                                                                                                                                                                                                                                                                                          
  public OpenTelemetry createOpenTelemetry(DatastoreOptions options) {                                                                                                                                                                                                                                                                                                                             
      // NEW: respect the opt-out flag                                                                                                                                                                                                                                                                                                                                                             
      if (!options.getOpenTelemetryOptions().isExportBuiltinMetricsToGoogleCloudMonitoring()) {                                                                                                                                                                                                                                                                                                    
          return OpenTelemetry.noop();                                                                                                                                                                                                                                                                                                                                                             
      }                                                                                                                                                                                                                                                                                                                                                                                            
      // ... existing logic                                                                                                                                                                                                                                                                                                                                                                        
  }          

Workaround

Until this is fixed, the only available workaround is to set the DATASTORE_EMULATOR_HOST system property to a non-empty dummy value before the DatastoreImpl is instantiated, then override spring.cloud.gcp.datastore.host (or equivalent) with the real endpoint to prevent the gRPC connection from being misdirected:

// In main() before application start                                                                                                                                                                                                                                                                                                                                                            
System.setProperty("DATASTORE_EMULATOR_HOST", "metrics-disabled");                                                                                                                                                                                                                                                                                                                               

This exploits the emulator detection path as a side effect, which is fragile and unintuitive.

Impact

This is particularly painful for local development and CI environments where ADC credentials exist but Cloud Monitoring IAM permissions are absent.

The warning fires every minute, clutters logs, and there is no documented, supported way to suppress it without resorting to the emulator side-channel.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions