Skip to content

Commit f90a43e

Browse files
committed
test(datastore): add Cloud Monitoring verification to IT and move config tests to unit tests
1 parent 6920311 commit f90a43e

2 files changed

Lines changed: 94 additions & 35 deletions

File tree

java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/DatastoreOptionsTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,4 +307,31 @@ public void testToBuilder() {
307307
assertNotEquals(original, newOptions);
308308
assertNotEquals(original.hashCode(), newOptions.hashCode());
309309
}
310+
@Test
311+
public void builtInMetricsExport_isDisabledByDefault() {
312+
DatastoreOptions defaultOptions =
313+
DatastoreOptions.newBuilder().setProjectId(PROJECT_ID).build();
314+
assertThat(
315+
defaultOptions
316+
.getOpenTelemetryOptions()
317+
.isExportBuiltinMetricsToGoogleCloudMonitoring())
318+
.isFalse();
319+
}
320+
321+
@Test
322+
public void bothBackendsActive_recorderIsComposite() {
323+
DatastoreOptions options =
324+
DatastoreOptions.newBuilder()
325+
.setProjectId(PROJECT_ID)
326+
.setDatabaseId(DATABASE_ID)
327+
.setOpenTelemetryOptions(
328+
DatastoreOpenTelemetryOptions.newBuilder()
329+
.setMetricsEnabled(true)
330+
.setExportBuiltinMetricsToGoogleCloudMonitoring(true)
331+
.build())
332+
.build();
333+
String recorderClassName =
334+
options.getMetricsRecorder().getClass().getSimpleName();
335+
assertThat(recorderClassName).isEqualTo("CompositeDatastoreMetricsRecorder");
336+
}
310337
}

java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/ITDatastoreBuiltInAndCustomMetrics.java

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
import io.opentelemetry.sdk.metrics.data.PointData;
3131
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
3232
import java.util.Arrays;
33+
import com.google.cloud.monitoring.v3.MetricServiceClient;
34+
import com.google.monitoring.v3.ListTimeSeriesRequest;
35+
import com.google.monitoring.v3.TimeInterval;
36+
import com.google.protobuf.Timestamp;
3337
import java.util.Collection;
3438
import java.util.Optional;
3539
import org.junit.After;
@@ -172,42 +176,9 @@ public void tearDown() throws Exception {
172176
}
173177
}
174178

175-
/**
176-
* Verifies that the Datastore client is configured with a composite recorder so that both the
177-
* built-in Cloud Monitoring backend and the user-provided custom OTel backend are active.
178-
*
179-
* <p>The composite recorder is the internal mechanism that fans out every {@code record*()} call
180-
* to all registered backends. Its presence guarantees that:
181-
*
182-
* <ul>
183-
* <li>the built-in SDK was successfully created and its exporter was wired up, AND
184-
* <li>the user-provided custom OTel instance is also receiving metric data.
185-
* </ul>
186-
*/
187-
@Test
188-
public void bothBackendsActive_recorderIsComposite() {
189-
// DatastoreOptions.getMetricsRecorder() is package-private; accessible because this test
190-
// lives in the same package (com.google.cloud.datastore).
191-
String recorderClassName =
192-
datastore.getOptions().getMetricsRecorder().getClass().getSimpleName();
193-
assertThat(recorderClassName).isEqualTo("CompositeDatastoreMetricsRecorder");
194-
}
195179

196-
/**
197-
* Verifies that the built-in metrics export flag is off by default. The flag is disabled until
198-
* the Datastore namespace in Cloud Monitoring is deployed; it must be explicitly opted in via
199-
* {@link DatastoreOpenTelemetryOptions.Builder#setExportBuiltinMetricsToGoogleCloudMonitoring}.
200-
*/
201-
@Test
202-
public void builtInMetricsExport_isDisabledByDefault() {
203-
DatastoreOptions defaultOptions =
204-
DatastoreOptions.newBuilder().setProjectId(PROJECT_ID).build();
205-
assertThat(
206-
defaultOptions
207-
.getOpenTelemetryOptions()
208-
.isExportBuiltinMetricsToGoogleCloudMonitoring())
209-
.isFalse();
210-
}
180+
181+
211182

212183
/**
213184
* Verifies that a transaction operation records {@code transaction_latency} and {@code
@@ -411,4 +382,65 @@ private static boolean dataContainsStringAttribute(
411382
String actual = point.getAttributes().get(AttributeKey.stringKey(attributeKey));
412383
return expectedValue.equals(actual);
413384
}
385+
@Test
386+
public void metricsExportedToCloudMonitoring() throws Exception {
387+
// Perform an operation to generate metrics
388+
Key key = datastore.newKeyFactory().setKind(kind).newKey("metrics-it-entity");
389+
Entity initial = Entity.newBuilder(key).set("value", 0L).build();
390+
datastore.put(initial);
391+
392+
datastore.runInTransaction(
393+
tx -> {
394+
Entity current = tx.get(key);
395+
tx.put(Entity.newBuilder(current).set("value", current.getLong("value") + 1).build());
396+
return null;
397+
});
398+
399+
// Wait for metrics to be flushed and ingested.
400+
// The default interval is 60 seconds, so we need to wait at least that long, plus ingestion delay.
401+
// Let's use a polling loop with a timeout of 150 seconds.
402+
403+
long startTimeMillis = System.currentTimeMillis();
404+
String filter = "metric.type = \"custom.googleapis.com/internal/client/transaction_latency\"";
405+
406+
boolean found = false;
407+
int attempts = 0;
408+
while (System.currentTimeMillis() - startTimeMillis < 150000) {
409+
attempts++;
410+
System.out.println("Checking Cloud Monitoring for metrics (attempt " + attempts + ")...");
411+
if (isMetricPresent(filter)) {
412+
found = true;
413+
break;
414+
}
415+
Thread.sleep(10000); // Wait 10 seconds between attempts
416+
}
417+
418+
assertWithMessage("Metrics should be present in Cloud Monitoring").that(found).isTrue();
419+
}
420+
421+
private boolean isMetricPresent(String filter) throws Exception {
422+
try (MetricServiceClient client = MetricServiceClient.create()) {
423+
String name = "projects/" + PROJECT_ID;
424+
425+
// Use a time interval covering the last 5 minutes
426+
long now = System.currentTimeMillis();
427+
Timestamp endTime = Timestamp.newBuilder().setSeconds(now / 1000).build();
428+
Timestamp startTime = Timestamp.newBuilder().setSeconds((now - 300000) / 1000).build();
429+
430+
TimeInterval interval = TimeInterval.newBuilder()
431+
.setStartTime(startTime)
432+
.setEndTime(endTime)
433+
.build();
434+
435+
ListTimeSeriesRequest request = ListTimeSeriesRequest.newBuilder()
436+
.setName(name)
437+
.setFilter(filter)
438+
.setInterval(interval)
439+
.setView(ListTimeSeriesRequest.TimeSeriesView.FULL)
440+
.build();
441+
442+
MetricServiceClient.ListTimeSeriesPagedResponse response = client.listTimeSeries(request);
443+
return response.iterateAll().iterator().hasNext();
444+
}
445+
}
414446
}

0 commit comments

Comments
 (0)