|
@@ -11,7 +11,6 @@ import org.elasticsearch.client.Request;
|
|
|
import org.elasticsearch.client.Response;
|
|
|
import org.elasticsearch.client.ResponseException;
|
|
|
import org.elasticsearch.client.RestClient;
|
|
|
-import org.elasticsearch.common.CheckedSupplier;
|
|
|
import org.elasticsearch.common.settings.Settings;
|
|
|
import org.elasticsearch.common.time.DateFormatter;
|
|
|
import org.elasticsearch.common.time.FormatNames;
|
|
@@ -19,7 +18,6 @@ import org.elasticsearch.common.xcontent.XContentHelper;
|
|
|
import org.elasticsearch.index.query.QueryBuilders;
|
|
|
import org.elasticsearch.logsdb.datageneration.matchers.MatchResult;
|
|
|
import org.elasticsearch.logsdb.datageneration.matchers.Matcher;
|
|
|
-import org.elasticsearch.rest.RestStatus;
|
|
|
import org.elasticsearch.search.aggregations.AggregationBuilders;
|
|
|
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
|
|
|
import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder;
|
|
@@ -29,7 +27,6 @@ import org.elasticsearch.test.ESTestCase;
|
|
|
import org.elasticsearch.xcontent.XContentBuilder;
|
|
|
import org.elasticsearch.xcontent.XContentFactory;
|
|
|
import org.elasticsearch.xcontent.XContentType;
|
|
|
-import org.hamcrest.Matchers;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
import java.time.Instant;
|
|
@@ -46,103 +43,34 @@ import static org.hamcrest.Matchers.equalTo;
|
|
|
import static org.hamcrest.Matchers.greaterThan;
|
|
|
|
|
|
/**
|
|
|
- * Basic challenge test - we index same documents into an index with standard index mode and an index with logsdb index mode.
|
|
|
- * Then we verify that results of common operations are the same modulo knows differences like synthetic source modifications.
|
|
|
- * This test uses simple mapping and document structure in order to allow easier debugging of the test itself.
|
|
|
+ * Challenge test (see {@link BulkStaticMappingChallengeRestIT}) that uses randomly generated
|
|
|
+ * mapping and documents in order to cover more code paths and permutations.
|
|
|
*/
|
|
|
-public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChallengeRestTest {
|
|
|
+public abstract class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChallengeRestTest {
|
|
|
+ protected final boolean fullyDynamicMapping = randomBoolean();
|
|
|
+ private final boolean useCustomSortConfig = fullyDynamicMapping == false && randomBoolean();
|
|
|
+ private final boolean routeOnSortFields = useCustomSortConfig && randomBoolean();
|
|
|
private final int numShards = randomBoolean() ? randomIntBetween(2, 4) : 0;
|
|
|
private final int numReplicas = randomBoolean() ? randomIntBetween(1, 3) : 0;
|
|
|
- private final boolean fullyDynamicMapping = randomBoolean();
|
|
|
+ protected final DataGenerationHelper dataGenerationHelper;
|
|
|
|
|
|
public StandardVersusLogsIndexModeChallengeRestIT() {
|
|
|
+ this(new DataGenerationHelper());
|
|
|
+ }
|
|
|
+
|
|
|
+ protected StandardVersusLogsIndexModeChallengeRestIT(DataGenerationHelper dataGenerationHelper) {
|
|
|
super("standard-apache-baseline", "logs-apache-contender", "baseline-template", "contender-template", 101, 101);
|
|
|
+ this.dataGenerationHelper = dataGenerationHelper;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void baselineMappings(XContentBuilder builder) throws IOException {
|
|
|
- if (fullyDynamicMapping == false) {
|
|
|
- builder.startObject()
|
|
|
- .startObject("properties")
|
|
|
-
|
|
|
- .startObject("@timestamp")
|
|
|
- .field("type", "date")
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .startObject("host.name")
|
|
|
- .field("type", "keyword")
|
|
|
- .field("ignore_above", randomIntBetween(1000, 1200))
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .startObject("message")
|
|
|
- .field("type", "keyword")
|
|
|
- .field("ignore_above", randomIntBetween(1000, 1200))
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .startObject("method")
|
|
|
- .field("type", "keyword")
|
|
|
- .field("ignore_above", randomIntBetween(1000, 1200))
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .startObject("memory_usage_bytes")
|
|
|
- .field("type", "long")
|
|
|
- .field("ignore_malformed", randomBoolean())
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .endObject();
|
|
|
- } else {
|
|
|
- // We want dynamic mapping, but we need host.name to be a keyword instead of text to support aggregations.
|
|
|
- builder.startObject()
|
|
|
- .startObject("properties")
|
|
|
-
|
|
|
- .startObject("host.name")
|
|
|
- .field("type", "keyword")
|
|
|
- .field("ignore_above", randomIntBetween(1000, 1200))
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .endObject()
|
|
|
- .endObject();
|
|
|
- }
|
|
|
+ dataGenerationHelper.standardMapping(builder);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void contenderMappings(XContentBuilder builder) throws IOException {
|
|
|
- builder.startObject();
|
|
|
- builder.field("subobjects", false);
|
|
|
-
|
|
|
- if (fullyDynamicMapping == false) {
|
|
|
- builder.startObject("properties")
|
|
|
-
|
|
|
- .startObject("@timestamp")
|
|
|
- .field("type", "date")
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .startObject("host.name")
|
|
|
- .field("type", "keyword")
|
|
|
- .field("ignore_above", randomIntBetween(1000, 1200))
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .startObject("message")
|
|
|
- .field("type", "keyword")
|
|
|
- .field("ignore_above", randomIntBetween(1000, 1200))
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .startObject("method")
|
|
|
- .field("type", "keyword")
|
|
|
- .field("ignore_above", randomIntBetween(1000, 1200))
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .startObject("memory_usage_bytes")
|
|
|
- .field("type", "long")
|
|
|
- .field("ignore_malformed", randomBoolean())
|
|
|
- .endObject()
|
|
|
-
|
|
|
- .endObject();
|
|
|
- }
|
|
|
-
|
|
|
- builder.endObject();
|
|
|
+ dataGenerationHelper.logsDbMapping(builder);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -159,6 +87,14 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
|
|
|
@Override
|
|
|
public void contenderSettings(Settings.Builder builder) {
|
|
|
builder.put("index.mode", "logsdb");
|
|
|
+ if (useCustomSortConfig) {
|
|
|
+ builder.putList("index.sort.field", "host.name", "method", "@timestamp");
|
|
|
+ builder.putList("index.sort.order", "asc", "asc", "desc");
|
|
|
+ if (routeOnSortFields) {
|
|
|
+ builder.put("index.logsdb.route_on_sort_fields", true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dataGenerationHelper.logsDbSettings(builder);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -169,6 +105,10 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
|
|
|
waitForLogs(client());
|
|
|
}
|
|
|
|
|
|
+ protected boolean autoGenerateId() {
|
|
|
+ return routeOnSortFields;
|
|
|
+ }
|
|
|
+
|
|
|
protected static void waitForLogs(RestClient client) throws Exception {
|
|
|
assertBusy(() -> {
|
|
|
try {
|
|
@@ -330,28 +270,6 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
|
|
|
assertTrue(matchResult.getMessage(), matchResult.isMatch());
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public Response indexBaselineDocuments(CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier) throws IOException {
|
|
|
- var response = super.indexBaselineDocuments(documentsSupplier);
|
|
|
-
|
|
|
- assertThat(response.getStatusLine().getStatusCode(), Matchers.equalTo(RestStatus.OK.getStatus()));
|
|
|
- var baselineResponseBody = entityAsMap(response);
|
|
|
- assertThat("errors in baseline bulk response:\n " + baselineResponseBody, baselineResponseBody.get("errors"), equalTo(false));
|
|
|
-
|
|
|
- return response;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Response indexContenderDocuments(CheckedSupplier<List<XContentBuilder>, IOException> documentsSupplier) throws IOException {
|
|
|
- var response = super.indexContenderDocuments(documentsSupplier);
|
|
|
-
|
|
|
- assertThat(response.getStatusLine().getStatusCode(), Matchers.equalTo(RestStatus.OK.getStatus()));
|
|
|
- var contenderResponseBody = entityAsMap(response);
|
|
|
- assertThat("errors in contender bulk response:\n " + contenderResponseBody, contenderResponseBody.get("errors"), equalTo(false));
|
|
|
-
|
|
|
- return response;
|
|
|
- }
|
|
|
-
|
|
|
private List<XContentBuilder> generateDocuments(int numberOfDocuments) throws IOException {
|
|
|
final List<XContentBuilder> documents = new ArrayList<>();
|
|
|
// This is static in order to be able to identify documents between test runs.
|
|
@@ -364,14 +282,12 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
|
|
|
}
|
|
|
|
|
|
protected XContentBuilder generateDocument(final Instant timestamp) throws IOException {
|
|
|
- return XContentFactory.jsonBuilder()
|
|
|
- .startObject()
|
|
|
- .field("@timestamp", DateFormatter.forPattern(FormatNames.STRICT_DATE_OPTIONAL_TIME.getName()).format(timestamp))
|
|
|
- .field("host.name", randomFrom("foo", "bar", "baz"))
|
|
|
- .field("message", randomFrom("a message", "another message", "still another message", "one more message"))
|
|
|
- .field("method", randomFrom("put", "post", "get"))
|
|
|
- .field("memory_usage_bytes", randomLongBetween(1000, 2000))
|
|
|
- .endObject();
|
|
|
+ var document = XContentFactory.jsonBuilder();
|
|
|
+ dataGenerationHelper.generateDocument(
|
|
|
+ document,
|
|
|
+ Map.of("@timestamp", DateFormatter.forPattern(FormatNames.STRICT_DATE_OPTIONAL_TIME.getName()).format(timestamp))
|
|
|
+ );
|
|
|
+ return document;
|
|
|
}
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@@ -383,7 +299,7 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
|
|
|
assertThat(hitsList.size(), greaterThan(0));
|
|
|
|
|
|
return hitsList.stream()
|
|
|
- .sorted(Comparator.comparingInt((Map<String, Object> hit) -> Integer.parseInt((String) hit.get("_id"))))
|
|
|
+ .sorted(Comparator.comparing((Map<String, Object> hit) -> ((String) hit.get("_id"))))
|
|
|
.map(hit -> (Map<String, Object>) hit.get("_source"))
|
|
|
.toList();
|
|
|
}
|
|
@@ -404,7 +320,7 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
|
|
|
|
|
|
// Results contain a list of [source, id] lists.
|
|
|
return values.stream()
|
|
|
- .sorted(Comparator.comparingInt((List<Object> value) -> Integer.parseInt((String) value.get(1))))
|
|
|
+ .sorted(Comparator.comparing((List<Object> value) -> ((String) value.get(1))))
|
|
|
.map(value -> (Map<String, Object>) value.get(0))
|
|
|
.toList();
|
|
|
}
|
|
@@ -437,4 +353,19 @@ public class StandardVersusLogsIndexModeChallengeRestIT extends AbstractChalleng
|
|
|
private void indexDocuments(List<XContentBuilder> documents) throws IOException {
|
|
|
indexDocuments(() -> documents, () -> documents);
|
|
|
}
|
|
|
+
|
|
|
+ protected final Map<String, Object> performBulkRequest(String json, boolean isBaseline) throws IOException {
|
|
|
+ var request = new Request("POST", "/" + (isBaseline ? getBaselineDataStreamName() : getContenderDataStreamName()) + "/_bulk");
|
|
|
+ request.setJsonEntity(json);
|
|
|
+ request.addParameter("refresh", "true");
|
|
|
+ var response = client.performRequest(request);
|
|
|
+ assertOK(response);
|
|
|
+ var responseBody = entityAsMap(response);
|
|
|
+ assertThat(
|
|
|
+ "errors in " + (isBaseline ? "baseline" : "contender") + " bulk response:\n " + responseBody,
|
|
|
+ responseBody.get("errors"),
|
|
|
+ equalTo(false)
|
|
|
+ );
|
|
|
+ return responseBody;
|
|
|
+ }
|
|
|
}
|