summaryrefslogtreecommitdiffstats
path: root/vespaclient-container-plugin/src/main/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'vespaclient-container-plugin/src/main/java/com')
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java94
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/documentapi/metrics/DocumentApiMetrics.java17
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/documentapi/metrics/DocumentOperationStatus.java2
3 files changed, 78 insertions, 35 deletions
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java
index 06cdccd17fc..6ad6fe2b1e2 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java
@@ -116,6 +116,7 @@ import static com.yahoo.jdisc.http.HttpRequest.Method.GET;
import static com.yahoo.jdisc.http.HttpRequest.Method.OPTIONS;
import static com.yahoo.jdisc.http.HttpRequest.Method.POST;
import static com.yahoo.jdisc.http.HttpRequest.Method.PUT;
+import static com.yahoo.vespa.http.server.Headers.CLIENT_VERSION;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.FINE;
@@ -449,7 +450,8 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
}
private ContentChannel postDocument(HttpRequest request, DocumentPath path, ResponseHandler rawHandler) {
- ResponseHandler handler = new MeasuringResponseHandler(rawHandler, com.yahoo.documentapi.metrics.DocumentOperationType.PUT, clock.instant());
+ ResponseHandler handler = new MeasuringResponseHandler(
+ request, rawHandler, com.yahoo.documentapi.metrics.DocumentOperationType.PUT, clock.instant());
if (getProperty(request, DRY_RUN, booleanParser).orElse(false)) {
handleFeedOperation(path, true, handler, new com.yahoo.documentapi.Response(-1));
return ignoredContent;
@@ -462,7 +464,7 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
DocumentOperationParameters parameters = parametersFromRequest(request, ROUTE)
.withResponseHandler(response -> {
outstanding.decrementAndGet();
- updatePutMetrics(response.outcome());
+ updatePutMetrics(response.outcome(), latencyOf(request));
handleFeedOperation(path, put.fullyApplied(), handler, response);
});
return () -> dispatchOperation(() -> asyncSession.put((DocumentPut)put.operation(), parameters));
@@ -471,7 +473,8 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
}
private ContentChannel putDocument(HttpRequest request, DocumentPath path, ResponseHandler rawHandler) {
- ResponseHandler handler = new MeasuringResponseHandler(rawHandler, com.yahoo.documentapi.metrics.DocumentOperationType.UPDATE, clock.instant());
+ ResponseHandler handler = new MeasuringResponseHandler(
+ request, rawHandler, com.yahoo.documentapi.metrics.DocumentOperationType.UPDATE, clock.instant());
if (getProperty(request, DRY_RUN, booleanParser).orElse(false)) {
handleFeedOperation(path, true, handler, new com.yahoo.documentapi.Response(-1));
return ignoredContent;
@@ -486,7 +489,7 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
DocumentOperationParameters parameters = parametersFromRequest(request, ROUTE)
.withResponseHandler(response -> {
outstanding.decrementAndGet();
- updateUpdateMetrics(response.outcome(), update.getCreateIfNonExistent());
+ updateUpdateMetrics(response.outcome(), latencyOf(request), update.getCreateIfNonExistent());
handleFeedOperation(path, parsed.fullyApplied(), handler, response);
});
return () -> dispatchOperation(() -> asyncSession.update(update, parameters));
@@ -495,7 +498,8 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
}
private ContentChannel deleteDocument(HttpRequest request, DocumentPath path, ResponseHandler rawHandler) {
- ResponseHandler handler = new MeasuringResponseHandler(rawHandler, com.yahoo.documentapi.metrics.DocumentOperationType.REMOVE, clock.instant());
+ ResponseHandler handler = new MeasuringResponseHandler(
+ request, rawHandler, com.yahoo.documentapi.metrics.DocumentOperationType.REMOVE, clock.instant());
if (getProperty(request, DRY_RUN, booleanParser).orElse(false)) {
handleFeedOperation(path, true, handler, new com.yahoo.documentapi.Response(-1));
return ignoredContent;
@@ -507,7 +511,7 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
DocumentOperationParameters parameters = parametersFromRequest(request, ROUTE)
.withResponseHandler(response -> {
outstanding.decrementAndGet();
- updateRemoveMetrics(response.outcome());
+ updateRemoveMetrics(response.outcome(), latencyOf(request));
handleFeedOperation(path, true, handler, response);
});
return () -> dispatchOperation(() -> asyncSession.remove(remove, parameters));
@@ -1090,33 +1094,50 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
handle(path, null, handler, response, (document, jsonResponse) -> jsonResponse.commit(Response.Status.OK, fullyApplied));
}
- private void updatePutMetrics(Outcome outcome) {
+ private static double latencyOf(HttpRequest r) { return (System.nanoTime() - r.relativeCreatedAtNanoTime()) / 1e+9d; }
+
+ private void updatePutMetrics(Outcome outcome, double latency) {
+ incrementMetricNumOperations(); incrementMetricNumPuts(); sampleLatency(latency);
switch (outcome) {
- case SUCCESS -> metric.add(MetricNames.SUCCEEDED, 1, null);
- case CONDITION_FAILED -> metric.add(MetricNames.CONDITION_NOT_MET, 1, null);
- default -> metric.add(MetricNames.FAILED, 1, null);
+ case SUCCESS -> incrementMetricSucceeded();
+ case CONDITION_FAILED -> { incrementMetricSucceeded(); incrementMetricConditionNotMet(); }
+ default -> incrementMetricFailed();
}
}
- private void updateUpdateMetrics(Outcome outcome, boolean create) {
+ private void updateUpdateMetrics(Outcome outcome, double latency, boolean create) {
if (create && outcome == Outcome.NOT_FOUND) outcome = Outcome.SUCCESS; // >_<
+ incrementMetricNumOperations(); incrementMetricNumUpdates(); sampleLatency(latency);
switch (outcome) {
- case SUCCESS -> metric.add(MetricNames.SUCCEEDED, 1, null);
- case NOT_FOUND -> metric.add(MetricNames.NOT_FOUND, 1, null);
- case CONDITION_FAILED -> metric.add(MetricNames.CONDITION_NOT_MET, 1, null);
- default -> metric.add(MetricNames.FAILED, 1, null);
+ case SUCCESS -> incrementMetricSucceeded();
+ case NOT_FOUND -> { incrementMetricSucceeded(); incrementMetricNotFound(); }
+ case CONDITION_FAILED -> { incrementMetricSucceeded(); incrementMetricConditionNotMet(); }
+ default -> incrementMetricFailed();
}
}
- private void updateRemoveMetrics(Outcome outcome) {
+ private void updateRemoveMetrics(Outcome outcome, double latency) {
+ incrementMetricNumOperations(); incrementMetricNumRemoves(); sampleLatency(latency);
switch (outcome) {
- case SUCCESS:
- case NOT_FOUND: metric.add(MetricNames.SUCCEEDED, 1, null); break;
- case CONDITION_FAILED: metric.add(MetricNames.CONDITION_NOT_MET, 1, null); break;
- default: metric.add(MetricNames.FAILED, 1, null); break;
+ case SUCCESS -> incrementMetricSucceeded();
+ case NOT_FOUND -> { incrementMetricSucceeded(); incrementMetricNotFound(); }
+ case CONDITION_FAILED -> { incrementMetricSucceeded(); incrementMetricConditionNotMet(); }
+ default -> incrementMetricFailed();
}
}
+ private void sampleLatency(double latency) { setMetric(MetricNames.LATENCY, latency); }
+ private void incrementMetricNumOperations() { incrementMetric(MetricNames.NUM_OPERATIONS); }
+ private void incrementMetricNumPuts() { incrementMetric(MetricNames.NUM_PUTS); }
+ private void incrementMetricNumRemoves() { incrementMetric(MetricNames.NUM_REMOVES); }
+ private void incrementMetricNumUpdates() { incrementMetric(MetricNames.NUM_UPDATES); }
+ private void incrementMetricFailed() { incrementMetric(MetricNames.FAILED); }
+ private void incrementMetricConditionNotMet() { incrementMetric(MetricNames.CONDITION_NOT_MET); }
+ private void incrementMetricSucceeded() { incrementMetric(MetricNames.SUCCEEDED); }
+ private void incrementMetricNotFound() { incrementMetric(MetricNames.NOT_FOUND); }
+ private void incrementMetric(String n) { metric.add(n, 1, null); }
+ private void setMetric(String n, Number v) { metric.set(n, v, null); }
+
// ------------------------------------------------- Visits ------------------------------------------------
private VisitorParameters parseGetParameters(HttpRequest request, DocumentPath path, boolean streamed) {
@@ -1438,8 +1459,13 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
private final ResponseHandler delegate;
private final com.yahoo.documentapi.metrics.DocumentOperationType type;
private final Instant start;
+ private final HttpRequest request;
- private MeasuringResponseHandler(ResponseHandler delegate, com.yahoo.documentapi.metrics.DocumentOperationType type, Instant start) {
+ private MeasuringResponseHandler(HttpRequest request,
+ ResponseHandler delegate,
+ com.yahoo.documentapi.metrics.DocumentOperationType type,
+ Instant start) {
+ this.request = request;
this.delegate = delegate;
this.type = type;
this.start = start;
@@ -1447,17 +1473,27 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
@Override
public ContentChannel handleResponse(Response response) {
- var statusCodeGroup = response.getStatus() / 100;
- // Status code 412 - condition not met - is considered OK
- if (statusCodeGroup == 2 || response.getStatus() == 412)
- metrics.reportSuccessful(type, start);
- else if (statusCodeGroup == 4)
- metrics.reportFailure(type, DocumentOperationStatus.REQUEST_ERROR);
- else if (statusCodeGroup == 5)
- metrics.reportFailure(type, DocumentOperationStatus.SERVER_ERROR);
+ switch (response.getStatus()) {
+ case 200 -> report(DocumentOperationStatus.OK);
+ case 400 -> report(DocumentOperationStatus.REQUEST_ERROR);
+ case 404 -> report(DocumentOperationStatus.NOT_FOUND);
+ case 412 -> report(DocumentOperationStatus.CONDITION_FAILED);
+ case 429 -> report(DocumentOperationStatus.TOO_MANY_REQUESTS);
+ case 500,502,503,504,507 -> report(DocumentOperationStatus.SERVER_ERROR);
+ default -> throw new IllegalStateException("Unexpected status code '%s'".formatted(response.getStatus()));
+ }
+ metrics.reportHttpRequest(clientVersion());
return delegate.handleResponse(response);
}
+ private void report(DocumentOperationStatus... status) { metrics.report(type, start, status); }
+
+ private String clientVersion() {
+ return Optional.ofNullable(request.headers().get(Headers.CLIENT_VERSION))
+ .filter(l -> !l.isEmpty()).map(l -> l.get(0))
+ .orElse("unknown");
+ }
+
}
static class StorageCluster {
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/documentapi/metrics/DocumentApiMetrics.java b/vespaclient-container-plugin/src/main/java/com/yahoo/documentapi/metrics/DocumentApiMetrics.java
index 890f06dd094..f1335351ac6 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/documentapi/metrics/DocumentApiMetrics.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/documentapi/metrics/DocumentApiMetrics.java
@@ -10,6 +10,7 @@ import com.yahoo.metrics.simple.Point;
import java.time.Duration;
import java.time.Instant;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@@ -52,16 +53,22 @@ public class DocumentApiMetrics {
feeds.add(point);
}
- public void reportSuccessful(DocumentOperationType documentOperationType, Instant startTime) {
- double latency = Duration.between(startTime, Instant.now()).toMillis() / 1000.0d;
- reportSuccessful(documentOperationType, latency);
- }
-
public void reportFailure(DocumentOperationType documentOperationType, DocumentOperationStatus documentOperationStatus) {
Point point = points.get(documentOperationStatus).get(documentOperationType);
feeds.add(point);
}
+ public void report(DocumentOperationType type, Instant start, DocumentOperationStatus... status) {
+ double latency = latency(start);
+ for (var s : status) {
+ var point = points.get(s).get(type);
+ feedLatency.sample(latency, point);
+ feeds.add(point);
+ }
+ }
+
+ private static double latency(Instant start) { return Duration.between(start, Instant.now()).toMillis() / 1000d; }
+
public void reportHttpRequest(String clientVersion) {
if (clientVersion != null) {
try {
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/documentapi/metrics/DocumentOperationStatus.java b/vespaclient-container-plugin/src/main/java/com/yahoo/documentapi/metrics/DocumentOperationStatus.java
index 97e5e9e63d8..f22eedeb5bc 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/documentapi/metrics/DocumentOperationStatus.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/documentapi/metrics/DocumentOperationStatus.java
@@ -17,7 +17,7 @@ import java.util.Set;
*/
public enum DocumentOperationStatus {
- OK, REQUEST_ERROR, SERVER_ERROR;
+ OK, REQUEST_ERROR, SERVER_ERROR, CONDITION_FAILED, NOT_FOUND, TOO_MANY_REQUESTS;
public static DocumentOperationStatus fromMessageBusErrorCodes(Set<Integer> errorCodes) {
if (errorCodes.size() == 1 && errorCodes.contains(DocumentProtocol.ERROR_NO_SPACE))