Skip to content

Commit 9b68b0c

Browse files
committed
Merge branch 'main' into downsampling++/add-basic-telemetry
2 parents 7aa255f + df8cedf commit 9b68b0c

File tree

6 files changed

+106
-6
lines changed

6 files changed

+106
-6
lines changed

docs/changelog/134231.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 134231
2+
summary: Fix unnecessary determinization in index pattern conflict checks
3+
area: Indices APIs
4+
type: bug
5+
issues:
6+
- 133652

docs/reference/query-languages/esql/images/functions/knn.svg

Lines changed: 1 addition & 1 deletion
Loading

muted-tests.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,6 @@ tests:
483483
- class: org.elasticsearch.xpack.esql.inference.rerank.RerankOperatorTests
484484
method: testSimpleCircuitBreaking
485485
issue: https://github.com/elastic/elasticsearch/issues/133619
486-
- class: org.elasticsearch.xpack.esql.qa.single_node.GenerativeIT
487-
method: test
488-
issue: https://github.com/elastic/elasticsearch/issues/133077
489486
- class: org.elasticsearch.xpack.kql.parser.KqlParserBooleanQueryTests
490487
method: testParseOrQuery
491488
issue: https://github.com/elastic/elasticsearch/issues/133863

server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,12 +1077,16 @@ static Map<String, List<String>> findConflictingV2Templates(
10771077
boolean checkPriority,
10781078
long priority
10791079
) {
1080-
Automaton v1automaton = Regex.simpleMatchToAutomaton(indexPatterns.toArray(Strings.EMPTY_ARRAY));
1080+
// No need to determinize the automaton, as it is only used to check for intersection with another automaton.
1081+
// Determinization is avoided because it can fail or become very costly due to state explosion.
1082+
Automaton v1automaton = Regex.simpleMatchToNonDeterminizedAutomaton(indexPatterns.toArray(Strings.EMPTY_ARRAY));
10811083
Map<String, List<String>> overlappingTemplates = new TreeMap<>();
10821084
for (Map.Entry<String, ComposableIndexTemplate> entry : templatesV2.entrySet()) {
10831085
String name = entry.getKey();
10841086
ComposableIndexTemplate template = entry.getValue();
1085-
Automaton v2automaton = Regex.simpleMatchToAutomaton(template.indexPatterns().toArray(Strings.EMPTY_ARRAY));
1087+
// No need to determinize the automaton, as it is only used to check for intersection with another automaton.
1088+
// Determinization is avoided because it can fail or become very costly due to state explosion.
1089+
Automaton v2automaton = Regex.simpleMatchToNonDeterminizedAutomaton(template.indexPatterns().toArray(Strings.EMPTY_ARRAY));
10861090
if (Operations.isEmpty(Operations.intersection(v1automaton, v2automaton)) == false) {
10871091
if (checkPriority == false || priority == template.priorityOrZero()) {
10881092
logger.debug(

server/src/test/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateServiceTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2526,6 +2526,23 @@ public void testV2TemplateOverlaps() throws Exception {
25262526
}
25272527
}
25282528

2529+
/**
2530+
* test that using complex index patterns doesn't run into a too_complex_to_determinize_exception,
2531+
* see https://github.com/elastic/elasticsearch/issues/133652
2532+
*/
2533+
public void testFindConflictingTemplates_complex_pattern() throws Exception {
2534+
ProjectMetadata initialProject = ProjectMetadata.builder(randomProjectIdOrDefault()).build();
2535+
List<String> complexPattern = new ArrayList<>();
2536+
for (int i = 1; i < 20; i++) {
2537+
complexPattern.add("cluster-somenamespace-*-app" + i + "*");
2538+
}
2539+
ComposableIndexTemplate template = ComposableIndexTemplate.builder().indexPatterns(complexPattern).build();
2540+
MetadataIndexTemplateService service = getMetadataIndexTemplateService();
2541+
ProjectMetadata project = service.addIndexTemplateV2(initialProject, false, "foo", template);
2542+
assertEquals(0, MetadataIndexTemplateService.findConflictingV1Templates(project, "foo", complexPattern).size());
2543+
assertEquals(0, MetadataIndexTemplateService.findConflictingV2Templates(project, "foo", complexPattern).size());
2544+
}
2545+
25292546
/**
25302547
* Tests to add two component templates but ignores both with is valid
25312548
*

x-pack/plugin/otel-data/src/javaRestTest/java/org/elasticsearch/xpack/oteldata/otlp/OTLPMetricsIndexingRestIT.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import io.opentelemetry.sdk.resources.Resource;
2626

2727
import org.elasticsearch.client.Request;
28+
import org.elasticsearch.common.hash.BufferedMurmur3Hasher;
2829
import org.elasticsearch.common.settings.SecureString;
2930
import org.elasticsearch.common.settings.Settings;
3031
import org.elasticsearch.common.util.concurrent.ThreadContext;
@@ -236,6 +237,81 @@ public void testCounterMonotonicity() throws Exception {
236237
assertThat(ObjectPath.evaluate(metrics, "up_down_counter_delta.time_series_metric"), equalTo("gauge"));
237238
}
238239

240+
public void testTsidForBulkIsSame() throws Exception {
241+
// This test is to ensure that the _tsid is the same when indexing via a bulk request or OTLP.
242+
long now = Clock.getDefault().now();
243+
244+
export(
245+
List.of(
246+
createDoubleGauge(
247+
TEST_RESOURCE,
248+
Attributes.builder()
249+
.put("string", "foo")
250+
.put("string_array", "foo", "bar", "baz")
251+
.put("boolean", true)
252+
.put("long", 42L)
253+
.put("double", 42.0)
254+
.put("host.ip", "127.0.0.1")
255+
.build(),
256+
"metric",
257+
42,
258+
"By",
259+
now
260+
)
261+
)
262+
);
263+
BufferedMurmur3Hasher hasher = new BufferedMurmur3Hasher(0);
264+
hasher.addString("metric");
265+
String metricNamesHash = Long.toHexString(hasher.digestHash().hashCode());
266+
// Index the same metric via a bulk request
267+
Request bulkRequest = new Request("POST", "metrics-generic.otel-default/_bulk?refresh");
268+
bulkRequest.setJsonEntity(
269+
"{\"create\":{}}\n"
270+
+ """
271+
{
272+
"@timestamp": $time,
273+
"start_timestamp": $time,
274+
"data_stream": {
275+
"type": "metrics",
276+
"dataset": "generic.otel",
277+
"namespace": "default"
278+
},
279+
"_metric_names_hash": "$metric_names_hash",
280+
"metrics": {
281+
"metric": 42.0
282+
},
283+
"attributes": {
284+
"string": "foo",
285+
"string_array": ["foo", "bar", "baz"],
286+
"boolean": true,
287+
"long": 42,
288+
"double": 42.0,
289+
"host.ip": "127.0.0.1"
290+
},
291+
"resource": {
292+
"attributes": {
293+
"service.name": "elasticsearch"
294+
}
295+
},
296+
"scope": {
297+
"name": "io.opentelemetry.example.metrics"
298+
},
299+
"unit": "By"
300+
}
301+
""".replace("\n", "")
302+
.replace("$time", Long.toString(TimeUnit.NANOSECONDS.toMillis(now) + 1))
303+
.replace("$metric_names_hash", metricNamesHash)
304+
+ "\n"
305+
);
306+
assertThat(ObjectPath.createFromResponse(client().performRequest(bulkRequest)).evaluate("errors"), equalTo(false));
307+
308+
ObjectPath searchResponse = ObjectPath.createFromResponse(
309+
client().performRequest(new Request("GET", "metrics-generic.otel-default/_search?docvalue_fields=_tsid"))
310+
);
311+
assertThat(searchResponse.evaluate("hits.total.value"), equalTo(2));
312+
assertThat(searchResponse.evaluate("hits.hits.0.fields._tsid"), equalTo(searchResponse.evaluate("hits.hits.1.fields._tsid")));
313+
}
314+
239315
private static Map<String, Object> getMapping(String target) throws IOException {
240316
Map<String, Object> mappings = ObjectPath.createFromResponse(client().performRequest(new Request("GET", target + "/_mapping")))
241317
.evaluate("");

0 commit comments

Comments
 (0)