Skip to content

Commit 9ba06b5

Browse files
authored
ESQL: Expand HeapAttack for LOOKUP (#120754) (#121448)
* ESQL: Expand HeapAttack for LOOKUP This expands the heap attack tests for LOOKUP. Now there are three flavors: 1. LOOKUP a single geo_point - about 30 bytes or so. 2. LOOKUP a one mb string. 3. LOOKUP no fields - just JOIN to alter cardinality. Fetching a geo_point is fine with about 500 repeated docs before it circuit breaks which works out to about 256mb of buffered results. That's sensible on our 512mb heap and likely to work ok for most folks. We'll flip to a streaming method eventually and this won't be a problem any more. But for now, we buffer. The no lookup fields is fine with like 7500 matches per incoming row. That's quite a lot, really. The 1mb string is trouble! We circuit break properly which is great and safe, but if you join 1mb worth of columns in LOOKUP you are going to need bigger heaps than our test. Again, we'll move from buffering these results to streaming them and it'll work better, but for now we buffer. * updates
1 parent 2b7d91a commit 9ba06b5

File tree

1 file changed

+83
-3
lines changed
  • test/external-modules/esql-heap-attack/src/javaRestTest/java/org/elasticsearch/xpack/esql/heap_attack

1 file changed

+83
-3
lines changed

test/external-modules/esql-heap-attack/src/javaRestTest/java/org/elasticsearch/xpack/esql/heap_attack/HeapAttackIT.java

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -628,24 +628,79 @@ private Response fetchMvLongs() throws IOException {
628628
}
629629

630630
public void testLookupExplosion() throws IOException {
631-
int sensorDataCount = 7500;
631+
int sensorDataCount = 500;
632632
int lookupEntries = 10000;
633633
Map<?, ?> map = lookupExplosion(sensorDataCount, lookupEntries);
634634
assertMap(map, matchesMap().extraOk().entry("values", List.of(List.of(sensorDataCount * lookupEntries))));
635635
}
636636

637637
public void testLookupExplosionManyMatches() throws IOException {
638638
assertCircuitBreaks(() -> {
639-
Map<?, ?> result = lookupExplosion(8500, 10000);
639+
Map<?, ?> result = lookupExplosion(1500, 10000);
640+
logger.error("should have failed but got {}", result);
641+
});
642+
}
643+
644+
public void testLookupExplosionNoFetch() throws IOException {
645+
int sensorDataCount = 7500;
646+
int lookupEntries = 10000;
647+
Map<?, ?> map = lookupExplosionNoFetch(sensorDataCount, lookupEntries);
648+
assertMap(map, matchesMap().extraOk().entry("values", List.of(List.of(sensorDataCount * lookupEntries))));
649+
}
650+
651+
public void testLookupExplosionNoFetchManyMatches() throws IOException {
652+
assertCircuitBreaks(() -> {
653+
Map<?, ?> result = lookupExplosionNoFetch(8500, 10000);
654+
logger.error("should have failed but got {}", result);
655+
});
656+
}
657+
658+
public void testLookupExplosionBigString() throws IOException {
659+
int sensorDataCount = 150;
660+
int lookupEntries = 1;
661+
Map<?, ?> map = lookupExplosionBigString(sensorDataCount, lookupEntries);
662+
assertMap(map, matchesMap().extraOk().entry("values", List.of(List.of(sensorDataCount * lookupEntries))));
663+
}
664+
665+
public void testLookupExplosionBigStringManyMatches() throws IOException {
666+
assertCircuitBreaks(() -> {
667+
Map<?, ?> result = lookupExplosionBigString(500, 1);
640668
logger.error("should have failed but got {}", result);
641669
});
642670
}
643671

644672
private Map<?, ?> lookupExplosion(int sensorDataCount, int lookupEntries) throws IOException {
673+
lookupExplosionData(sensorDataCount, lookupEntries);
674+
StringBuilder query = startQuery();
675+
query.append("FROM sensor_data | LOOKUP JOIN sensor_lookup ON id | STATS COUNT(location)\"}");
676+
return responseAsMap(query(query.toString(), null));
677+
}
678+
679+
private Map<?, ?> lookupExplosionNoFetch(int sensorDataCount, int lookupEntries) throws IOException {
680+
lookupExplosionData(sensorDataCount, lookupEntries);
681+
StringBuilder query = startQuery();
682+
query.append("FROM sensor_data | LOOKUP JOIN sensor_lookup ON id | STATS COUNT(*)\"}");
683+
return responseAsMap(query(query.toString(), null));
684+
}
685+
686+
private void lookupExplosionData(int sensorDataCount, int lookupEntries) throws IOException {
645687
initSensorData(sensorDataCount, 1);
646688
initSensorLookup(lookupEntries, 1, i -> "73.9857 40.7484");
689+
}
690+
691+
private Map<?, ?> lookupExplosionBigString(int sensorDataCount, int lookupEntries) throws IOException {
692+
initSensorData(sensorDataCount, 1);
693+
initSensorLookupString(lookupEntries, 1, i -> {
694+
int target = Math.toIntExact(ByteSizeValue.ofMb(1).getBytes());
695+
StringBuilder str = new StringBuilder(Math.toIntExact(ByteSizeValue.ofMb(2).getBytes()));
696+
while (str.length() < target) {
697+
str.append("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
698+
}
699+
logger.info("big string is {} characters", str.length());
700+
return str.toString();
701+
});
647702
StringBuilder query = startQuery();
648-
query.append("FROM sensor_data | LOOKUP JOIN sensor_lookup ON id | STATS COUNT(*)\"}");
703+
query.append("FROM sensor_data | LOOKUP JOIN sensor_lookup ON id | STATS COUNT(string)\"}");
649704
return responseAsMap(query(query.toString(), null));
650705
}
651706

@@ -834,6 +889,31 @@ private void initSensorLookup(int lookupEntries, int sensorCount, IntFunction<St
834889
initIndex("sensor_lookup", data.toString());
835890
}
836891

892+
private void initSensorLookupString(int lookupEntries, int sensorCount, IntFunction<String> string) throws IOException {
893+
logger.info("loading sensor lookup with huge strings");
894+
createIndex("sensor_lookup", Settings.builder().put(IndexSettings.MODE.getKey(), IndexMode.LOOKUP.getName()).build(), """
895+
{
896+
"properties": {
897+
"id": { "type": "long" },
898+
"string": { "type": "text" }
899+
}
900+
}""");
901+
int docsPerBulk = 10;
902+
StringBuilder data = new StringBuilder();
903+
for (int i = 0; i < lookupEntries; i++) {
904+
int sensor = i % sensorCount;
905+
data.append(String.format(Locale.ROOT, """
906+
{"create":{}}
907+
{"id": %d, "string": "%s"}
908+
""", sensor, string.apply(sensor)));
909+
if (i % docsPerBulk == docsPerBulk - 1) {
910+
bulk("sensor_lookup", data.toString());
911+
data.setLength(0);
912+
}
913+
}
914+
initIndex("sensor_lookup", data.toString());
915+
}
916+
837917
private void initSensorEnrich(int lookupEntries, int sensorCount, IntFunction<String> location) throws IOException {
838918
initSensorLookup(lookupEntries, sensorCount, location);
839919
logger.info("loading sensor enrich");

0 commit comments

Comments
 (0)