Skip to content

Commit

Permalink
record serverSicdeTransaction
Browse files Browse the repository at this point in the history
  • Loading branch information
milaGGL committed Oct 22, 2024
1 parent 4a2a8d8 commit 47f6b37
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 2 deletions.
15 changes: 15 additions & 0 deletions google-cloud-firestore/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,21 @@
<!-- END OpenTelemetry -->

<!-- Test dependencies -->
<!-- SLF4J for logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.google.api.gax.retrying.ExponentialRetryAlgorithm;
import com.google.api.gax.retrying.TimedAttemptSettings;
import com.google.api.gax.rpc.ApiException;
import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
import com.google.cloud.firestore.telemetry.TelemetryConstants;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
Expand Down Expand Up @@ -60,6 +61,7 @@ final class ServerSideTransactionRunner<T> {
private int attemptsRemaining;
private Span runTransactionSpan;
private TraceUtil.Context runTransactionContext;
private MetricsContext metricsContext;

/**
* @param firestore The active Firestore instance
Expand All @@ -86,14 +88,27 @@ final class ServerSideTransactionRunner<T> {
new ExponentialRetryAlgorithm(
firestore.getOptions().getRetrySettings(), CurrentMillisClock.getDefaultClock());
this.nextBackoffAttempt = backoffAlgorithm.createFirstAttempt();
this.metricsContext =
firestore.getOptions().getMetricsUtil().createMetricsContext("ServerSideTransaction");
}

@Nonnull
private TraceUtil getTraceUtil() {
return firestore.getOptions().getTraceUtil();
}

private int attemptsMade() {
return transactionOptions.getNumberOfAttempts() - this.attemptsRemaining;
}

ApiFuture<T> run() {
ApiFuture<T> result = runInternally();
metricsContext.recordTransactionLatencyAtFuture(result);
metricsContext.recordTransactionAttemptsAtFuture(result, () -> this.attemptsMade());
return result;
}

ApiFuture<T> runInternally() {
runTransactionSpan = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_TRANSACTION_RUN);
runTransactionSpan.setAttribute(
ATTRIBUTE_KEY_TRANSACTION_TYPE, transactionOptions.getType().name());
Expand Down Expand Up @@ -237,7 +252,7 @@ private ApiFuture<T> restartTransactionCallback(Throwable throwable) {
getTraceUtil()
.currentSpan()
.addEvent("Initiating transaction retry. Attempts remaining: " + attemptsRemaining);
return run();
return runInternally();
} else {
final FirestoreException firestoreException =
FirestoreException.forApiException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.api.core.ApiFuture;
import com.google.api.gax.tracing.ApiTracerFactory;
import java.util.List;
import java.util.function.Supplier;

/**
* A fully disabled (No-op) MetricsUtil class that does not perform any metrics collection actions
Expand All @@ -39,6 +40,13 @@ public void recordEndToEndLatency(Throwable t) {}

@Override
public void recordFirstResponseLatency() {}

@Override
public <T> void recordTransactionLatencyAtFuture(ApiFuture<T> futureValue) {}

@Override
public <T> void recordTransactionAttemptsAtFuture(
ApiFuture<T> futureValue, Supplier<Integer> attemptsSupplier) {}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -200,6 +201,55 @@ private void recordEndToEndLatency(String status) {
customMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes);
}

public <T> void recordTransactionLatencyAtFuture(ApiFuture<T> futureValue) {
ApiFutures.addCallback(
futureValue,
new ApiFutureCallback<T>() {
@Override
public void onFailure(Throwable t) {
recordTransactionLatency(extractErrorStatus(t));
}

@Override
public void onSuccess(T result) {

recordTransactionLatency(StatusCode.Code.OK.toString());
}
},
MoreExecutors.directExecutor());
}

private void recordTransactionLatency(String status) {
double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS);
Map<String, String> attributes = createAttributes(status);
defaultMetricsProvider.transactionLatencyRecorder(elapsedTime, attributes);
customMetricsProvider.transactionLatencyRecorder(elapsedTime, attributes);
}

public <T> void recordTransactionAttemptsAtFuture(
ApiFuture<T> futureValue, Supplier<Integer> attemptsSupplier) {
ApiFutures.addCallback(
futureValue,
new ApiFutureCallback<T>() {
@Override
public void onFailure(Throwable t) {
recordTransactionAttempts(extractErrorStatus(t), attemptsSupplier.get());
}

@Override
public void onSuccess(T result) {
recordTransactionAttempts(StatusCode.Code.OK.toString(), attemptsSupplier.get());
}
},
MoreExecutors.directExecutor());
}

private void recordTransactionAttempts(String status, int attempts) {
Map<String, String> attributes = createAttributes(status);
defaultMetricsProvider.transactionAttemptCountRecorder((long) attempts, attributes);
customMetricsProvider.transactionAttemptCountRecorder((long) attempts, attributes);
}

private Map<String, String> createAttributes(String status) {
Map<String, String> attributes = new HashMap<>();
attributes.put(METRIC_ATTRIBUTE_KEY_METHOD.getKey(), methodName);
Expand All @@ -211,6 +261,7 @@ private String extractErrorStatus(@Nullable Throwable throwable) {
if (!(throwable instanceof FirestoreException)) {
return StatusCode.Code.UNKNOWN.toString();
}

Status status = ((FirestoreException) throwable).getStatus();
if (status == null) {
return StatusCode.Code.UNKNOWN.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.api.gax.tracing.ApiTracerFactory;
import com.google.cloud.firestore.FirestoreOptions;
import java.util.List;
import java.util.function.Supplier;
import java.util.logging.Logger;
import javax.annotation.Nonnull;

Expand Down Expand Up @@ -55,7 +56,7 @@ static boolean shouldCreateEnabledInstance() {
// Client side metrics feature is default on unless it is manually turned off by
// environment variables
// TODO(metrics): The feature is disabled before it is ready for general release.
boolean shouldCreateEnabledInstance = false;
boolean shouldCreateEnabledInstance = true;

String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR);
if (enableMetricsEnvVar != null) {
Expand Down Expand Up @@ -109,5 +110,15 @@ interface MetricsContext {

/** Records first response latency for the current operation. */
void recordFirstResponseLatency();

/**
* Transaction latency should be recorded _after_ all the operations, including the retires has
* been completed. This method "appends" the metrics recording code at the completion of the
* given future.
*/
<T> void recordTransactionLatencyAtFuture(ApiFuture<T> futureValue);

<T> void recordTransactionAttemptsAtFuture(
ApiFuture<T> futureValue, Supplier<Integer> attemptsSupplier);
}
}

0 comments on commit 47f6b37

Please sign in to comment.