Skip to content

Commit

Permalink
Add upload stats service
Browse files Browse the repository at this point in the history
This service will build final XContent for stats from upload API
across Nodes.

Signed-off-by: Vijayan Balasubramanian <balasvij@amazon.com>
  • Loading branch information
VijayanB committed May 12, 2022
1 parent 4ac91b5 commit 6e29f7e
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public TotalUploadStats(final List<UploadStats> uploadStatsList) {

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {

builder.startObject(FIELDS.TOTAL.toString());
if (isUploadStatsEmpty()) {
return builder.endObject();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.geospatial.stats.upload;

import java.io.IOException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import org.opensearch.common.xcontent.ToXContentFragment;
import org.opensearch.common.xcontent.XContentBuilder;

// Service to calculate summary of upload stats and generate XContent for StatsResponse
public class UploadStatsService implements ToXContentFragment {

public static final String UPLOADS = "uploads";
public static final String TOTAL = "total";
public static final String METRICS = "metrics";
public static final String NODE_ID = "node_id";
private final Map<String, UploadStats> uploadStats;
private final TotalUploadStats totalUploadStats;

public UploadStatsService(Map<String, UploadStats> uploadStats) {
this.uploadStats = Objects.requireNonNull(uploadStats, "upload stats map cannot be null");
this.totalUploadStats = new TotalUploadStats(new ArrayList<>(uploadStats.values()));
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
/*
{
"uploads": {
"total": {
request_count : # of request,
"upload" : sum of documents to upload across API,
"success" : sum of successfully uploaded documents across API,
"failed" : sum of failed to upload documents across API,
"duration" : sum of duration in milliseconds to ingest document across API
},
"metrics" : [
{
"id" : <metric-id>,
"node_id" : node-id
"upload" : # of documents to upload,
"success" : # of successfully uploaded documents,
"failed" : # of failed to upload documents,
"duration" : duration in milliseconds to ingest document
}, ......
]
}
}
*/
builder.startObject(UPLOADS);
totalUploadStats.toXContent(builder, params);
builder.startArray(METRICS);
if (totalUploadStats.isUploadStatsEmpty()) {
builder.endArray();
return builder.endObject();
}
final List<AbstractMap.SimpleEntry<String, UploadMetric>> metricsByNodeID = groupMetricsByNodeID(uploadStats);
for (AbstractMap.SimpleEntry<String, UploadMetric> entry : metricsByNodeID) {
addMetrics(builder, params, entry);
}
builder.endArray();
return builder.endObject();
}

private void addMetrics(XContentBuilder builder, Params params, AbstractMap.SimpleEntry<String, UploadMetric> entry)
throws IOException {
builder.startObject();
builder.field(NODE_ID, entry.getKey());
entry.getValue().toXContent(builder, params);
builder.endObject();
}

private List<AbstractMap.SimpleEntry<String, UploadMetric>> groupMetricsByNodeID(Map<String, UploadStats> uploadStats) {
return uploadStats.entrySet()
.stream()
.flatMap(stat -> stat.getValue().getMetrics().stream().map(metric -> new AbstractMap.SimpleEntry<>(stat.getKey(), metric)))
.collect(Collectors.toList());

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,17 @@
package org.opensearch.geospatial.stats.upload;

import static org.opensearch.geospatial.GeospatialTestHelper.buildFieldNameValuePair;
import static org.opensearch.geospatial.stats.upload.UploadStatsBuilder.randomUploadStats;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;

import org.opensearch.common.Strings;
import org.opensearch.common.xcontent.ToXContent;
import org.opensearch.common.xcontent.XContentBuilder;
import org.opensearch.common.xcontent.XContentFactory;
import org.opensearch.common.xcontent.XContentType;
import org.opensearch.geospatial.GeospatialTestHelper;
import org.opensearch.test.OpenSearchTestCase;

public class TotalUploadStatsTests extends OpenSearchTestCase {
Expand All @@ -35,19 +33,6 @@ public class TotalUploadStatsTests extends OpenSearchTestCase {

private static final long INIT = 0L;

private UploadStats randomUploadStats() {
int randomMetricCount = randomIntBetween(MIN_METRIC_COUNT, MAX_METRIC_COUNT);
UploadStats stats = new UploadStats();
IntStream.range(0, randomMetricCount).forEach(unUsed -> stats.addMetric(GeospatialTestHelper.generateRandomUploadMetric()));
return stats;
}

private List<UploadStats> randomUploadStats(int max) {
List<UploadStats> stats = new ArrayList<>();
IntStream.range(0, max).forEach(unUsed -> stats.add(randomUploadStats()));
return stats;
}

public void testInstanceCreation() {
int randomStatsCount = randomIntBetween(MIN_STATS_COUNT, MAX_STATS_COUNT);
TotalUploadStats totalUploadStats = new TotalUploadStats(randomUploadStats(randomStatsCount));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.geospatial.stats.upload;

import static org.opensearch.test.OpenSearchTestCase.randomIntBetween;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

import org.opensearch.geospatial.GeospatialTestHelper;

public class UploadStatsBuilder {
private static final int MAX_METRIC_COUNT = 10;
private static final int MIN_METRIC_COUNT = 2;

public static UploadStats randomUploadStats() {
int randomMetricCount = randomIntBetween(MIN_METRIC_COUNT, MAX_METRIC_COUNT);
UploadStats stats = new UploadStats();
IntStream.range(0, randomMetricCount).forEach(unUsed -> stats.addMetric(GeospatialTestHelper.generateRandomUploadMetric()));
return stats;
}

public static List<UploadStats> randomUploadStats(int max) {
List<UploadStats> stats = new ArrayList<>();
IntStream.range(0, max).forEach(unUsed -> stats.add(randomUploadStats()));
return stats;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.geospatial.stats.upload;

import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.opensearch.geospatial.GeospatialTestHelper.buildFieldNameValuePair;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.opensearch.common.Strings;
import org.opensearch.common.xcontent.ToXContent;
import org.opensearch.common.xcontent.XContentBuilder;
import org.opensearch.geospatial.GeospatialTestHelper;
import org.opensearch.test.OpenSearchTestCase;

public class UploadStatsServiceTests extends OpenSearchTestCase {

private String removeStartAndEndObject(String content) {
assertNotNull(content);
assertTrue("content length should be at least 2", content.length() > 1);
return content.substring(1, content.length() - 1);
}

public void testInstanceCreation() {
Map<String, UploadStats> randomMap = new HashMap<>();
randomMap.put(GeospatialTestHelper.randomLowerCaseString(), UploadStatsBuilder.randomUploadStats());
randomMap.put(GeospatialTestHelper.randomLowerCaseString(), UploadStatsBuilder.randomUploadStats());

UploadStatsService service = new UploadStatsService(randomMap);
assertNotNull(service);
}

public void testInstanceCreationFails() {
assertThrows(NullPointerException.class, () -> new UploadStatsService(null));
}

public void testXContentWithNodeID() {
Map<String, UploadStats> randomMap = new HashMap<>();
randomMap.put(GeospatialTestHelper.randomLowerCaseString(), UploadStatsBuilder.randomUploadStats());
randomMap.put(GeospatialTestHelper.randomLowerCaseString(), UploadStatsBuilder.randomUploadStats());
UploadStatsService service = new UploadStatsService(randomMap);
String xContent = Strings.toString(service);
assertNotNull(xContent);
for (String nodeID : randomMap.keySet()) {
assertTrue(nodeID + " is missing", xContent.contains(buildFieldNameValuePair(UploadStatsService.NODE_ID, nodeID)));
}
}

public void testXContentWithEmptyStats() throws IOException {
UploadStatsService service = new UploadStatsService(new HashMap<>());
final XContentBuilder contentBuilder = jsonBuilder().startObject();
service.toXContent(contentBuilder, ToXContent.EMPTY_PARAMS);
contentBuilder.endObject();
String emptyContent = "{\"uploads\":{\"total\":{},\"metrics\":[]}}";
assertEquals(emptyContent, Strings.toString(contentBuilder));
}

public void testXContentWithTotalUploadStats() throws IOException {
Map<String, UploadStats> randomMap = new HashMap<>();
final List<UploadStats> uploadStats = List.of(UploadStatsBuilder.randomUploadStats(), UploadStatsBuilder.randomUploadStats());

for (UploadStats stats : uploadStats) {
randomMap.put(GeospatialTestHelper.randomLowerCaseString(), stats);
}
UploadStatsService service = new UploadStatsService(randomMap);
final XContentBuilder serviceContentBuilder = jsonBuilder().startObject();
service.toXContent(serviceContentBuilder, ToXContent.EMPTY_PARAMS);
serviceContentBuilder.endObject();
String content = Strings.toString(serviceContentBuilder);
assertNotNull(content);

final XContentBuilder summary = jsonBuilder().startObject();
TotalUploadStats expectedSummary = new TotalUploadStats(uploadStats);
expectedSummary.toXContent(summary, ToXContent.EMPTY_PARAMS);
summary.endObject();
final String totalUploadStatsSummary = Strings.toString(summary);
assertNotNull(totalUploadStatsSummary);
assertTrue(content.contains(removeStartAndEndObject(totalUploadStatsSummary)));
}

public void testXContentWithMetrics() throws IOException {
Map<String, UploadStats> randomMap = new HashMap<>();
List<UploadMetric> randomMetrics = new ArrayList<>();
final List<UploadStats> uploadStats = List.of(UploadStatsBuilder.randomUploadStats(), UploadStatsBuilder.randomUploadStats());
for (UploadStats stats : uploadStats) {
randomMap.put(GeospatialTestHelper.randomLowerCaseString(), stats);
randomMetrics.addAll(stats.getMetrics());
}
UploadStatsService service = new UploadStatsService(randomMap);
final XContentBuilder serviceContentBuilder = jsonBuilder().startObject();
service.toXContent(serviceContentBuilder, ToXContent.EMPTY_PARAMS);
serviceContentBuilder.endObject();
String content = Strings.toString(serviceContentBuilder);
assertNotNull(content);

for (UploadMetric metric : randomMetrics) {
XContentBuilder metricsAsContent = jsonBuilder().startObject();
metric.toXContent(metricsAsContent, ToXContent.EMPTY_PARAMS);
metricsAsContent.endObject();
final String metricsAsString = Strings.toString(metricsAsContent);
assertNotNull(metricsAsString);
assertTrue(content.contains(removeStartAndEndObject(metricsAsString)));
}
}
}

0 comments on commit 6e29f7e

Please sign in to comment.