Skip to content

Commit 5ab81a9

Browse files
authored
Do not serialize EsIndex in plan (#119580)
Certain plan classes (such as EsRelation, EsSourceExec, EsQueryExec) contain and serialize the entire EsIndex instance. This instance might contain huge mapping that is never used in plan. This change replaces EsIndex usage with indexPattern and indexNameWithModes to minimize the size of the serialized plan.
1 parent 3fa2415 commit 5ab81a9

29 files changed

+398
-209
lines changed

docs/changelog/119580.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 119580
2+
summary: Do not serialize `EsIndex` in plan
3+
area: ES|QL
4+
type: enhancement
5+
issues: []

server/src/main/java/org/elasticsearch/TransportVersions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ static TransportVersion def(int id) {
158158
public static final TransportVersion ESQL_PROFILE_ROWS_PROCESSED = def(8_824_00_0);
159159
public static final TransportVersion BYTE_SIZE_VALUE_ALWAYS_USES_BYTES_1 = def(8_825_00_0);
160160
public static final TransportVersion REVERT_BYTE_SIZE_VALUE_ALWAYS_USES_BYTES_1 = def(8_826_00_0);
161+
public static final TransportVersion ESQL_SKIP_ES_INDEX_SERIALIZATION = def(8_827_00_0);
161162

162163
/*
163164
* STOP! READ THIS FIRST! No, really,

x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ public static Range rangeOf(Expression value, Expression lower, boolean includeL
215215
}
216216

217217
public static EsRelation relation() {
218-
return new EsRelation(EMPTY, new EsIndex(randomAlphaOfLength(8), emptyMap()), IndexMode.STANDARD, randomBoolean());
218+
return new EsRelation(EMPTY, new EsIndex(randomAlphaOfLength(8), emptyMap()), IndexMode.STANDARD);
219219
}
220220

221221
/**

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,13 @@ private LogicalPlan resolveIndex(UnresolvedRelation plan, IndexResolution indexR
269269
}
270270
var attributes = mappingAsAttributes(plan.source(), esIndex.mapping());
271271
attributes.addAll(plan.metadataFields());
272-
return new EsRelation(plan.source(), esIndex, attributes.isEmpty() ? NO_FIELDS : attributes, plan.indexMode());
272+
return new EsRelation(
273+
plan.source(),
274+
esIndex.name(),
275+
plan.indexMode(),
276+
esIndex.indexNameWithModes(),
277+
attributes.isEmpty() ? NO_FIELDS : attributes
278+
);
273279
}
274280
}
275281

@@ -1371,9 +1377,13 @@ private LogicalPlan doRule(LogicalPlan plan) {
13711377
}
13721378

13731379
if (missing.isEmpty() == false) {
1374-
List<Attribute> newOutput = new ArrayList<>(esr.output());
1375-
newOutput.addAll(missing);
1376-
return new EsRelation(esr.source(), esr.index(), newOutput, esr.indexMode(), esr.frozen());
1380+
return new EsRelation(
1381+
esr.source(),
1382+
esr.indexPattern(),
1383+
esr.indexMode(),
1384+
esr.indexNameWithModes(),
1385+
CollectionUtils.combine(esr.output(), missing)
1386+
);
13771387
}
13781388
return esr;
13791389
});

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PruneColumns.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,13 @@ public LogicalPlan apply(LogicalPlan plan) {
102102
p = new Eval(eval.source(), eval.child(), remaining);
103103
}
104104
}
105-
} else if (p instanceof EsRelation esRelation && esRelation.indexMode() == IndexMode.LOOKUP) {
105+
} else if (p instanceof EsRelation esr && esr.indexMode() == IndexMode.LOOKUP) {
106106
// Normally, pruning EsRelation has no effect because InsertFieldExtraction only extracts the required fields, anyway.
107107
// However, InsertFieldExtraction can't be currently used in LOOKUP JOIN right index,
108108
// it works differently as we extract all fields (other than the join key) that the EsRelation has.
109-
var remaining = removeUnused(esRelation.output(), used);
109+
var remaining = removeUnused(esr.output(), used);
110110
if (remaining != null) {
111-
p = new EsRelation(esRelation.source(), esRelation.index(), remaining, esRelation.indexMode(), esRelation.frozen());
111+
p = new EsRelation(esr.source(), esr.indexPattern(), esr.indexMode(), esr.indexNameWithModes(), remaining);
112112
}
113113
}
114114
} while (recheck);

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/SkipQueryOnEmptyMappings.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ public final class SkipQueryOnEmptyMappings extends OptimizerRules.OptimizerRule
1616

1717
@Override
1818
protected LogicalPlan rule(EsRelation plan) {
19-
return plan.index().concreteIndices().isEmpty() ? new LocalRelation(plan.source(), plan.output(), LocalSupplier.EMPTY) : plan;
19+
return plan.concreteIndices().isEmpty() ? new LocalRelation(plan.source(), plan.output(), LocalSupplier.EMPTY) : plan;
2020
}
2121
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/TranslateMetricsAggregate.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ private static Aggregate toStandardAggregate(Aggregate metrics) {
220220
if (attributes.stream().noneMatch(a -> a.name().equals(MetadataAttribute.TIMESTAMP_FIELD))) {
221221
attributes.removeIf(a -> a.name().equals(MetadataAttribute.TIMESTAMP_FIELD));
222222
}
223-
return new EsRelation(r.source(), r.index(), new ArrayList<>(attributes), IndexMode.STANDARD);
223+
return new EsRelation(r.source(), r.indexPattern(), IndexMode.STANDARD, r.indexNameWithModes(), new ArrayList<>(attributes));
224224
});
225225
return new Aggregate(metrics.source(), child, Aggregate.AggregateType.STANDARD, metrics.groupings(), metrics.aggregates());
226226
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/PushFiltersToSource.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,9 @@ private static PhysicalPlan rewrite(
104104
var query = Queries.combine(Queries.Clause.FILTER, asList(queryExec.query(), planQuery));
105105
queryExec = new EsQueryExec(
106106
queryExec.source(),
107-
queryExec.index(),
107+
queryExec.indexPattern(),
108108
queryExec.indexMode(),
109+
queryExec.indexNameWithModes(),
109110
queryExec.output(),
110111
query,
111112
queryExec.limit(),

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/PushStatsToSource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ protected PhysicalPlan rule(AggregateExec aggregateExec, LocalPhysicalOptimizerC
5959
if (tuple.v2().size() == aggregateExec.aggregates().size()) {
6060
plan = new EsStatsQueryExec(
6161
aggregateExec.source(),
62-
queryExec.index(),
62+
queryExec.indexPattern(),
6363
queryExec.query(),
6464
queryExec.limit(),
6565
tuple.v1(),

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/ReplaceSourceAttributes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@ protected PhysicalPlan rule(EsSourceExec plan) {
5353
attributes.add(ma);
5454
}
5555
});
56-
return new EsQueryExec(plan.source(), plan.index(), plan.indexMode(), attributes, plan.query());
56+
return new EsQueryExec(plan.source(), plan.indexPattern(), plan.indexMode(), plan.indexNameWithModes(), attributes, plan.query());
5757
}
5858
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/EsRelation.java

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
import java.util.Map;
2828
import java.util.Map.Entry;
2929
import java.util.Objects;
30+
import java.util.Set;
31+
32+
import static org.elasticsearch.TransportVersions.ESQL_SKIP_ES_INDEX_SERIALIZATION;
3033

3134
public class EsRelation extends LeafPlan {
3235
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
@@ -35,30 +38,41 @@ public class EsRelation extends LeafPlan {
3538
EsRelation::readFrom
3639
);
3740

38-
private final EsIndex index;
39-
private final List<Attribute> attrs;
40-
private final boolean frozen;
41+
private final String indexPattern;
4142
private final IndexMode indexMode;
43+
private final Map<String, IndexMode> indexNameWithModes;
44+
private final List<Attribute> attrs;
4245

43-
public EsRelation(Source source, EsIndex index, IndexMode indexMode, boolean frozen) {
44-
this(source, index, flatten(source, index.mapping()), indexMode, frozen);
45-
}
46-
47-
public EsRelation(Source source, EsIndex index, List<Attribute> attributes, IndexMode indexMode) {
48-
this(source, index, attributes, indexMode, false);
46+
public EsRelation(Source source, EsIndex index, IndexMode indexMode) {
47+
this(source, index.name(), indexMode, index.indexNameWithModes(), flatten(source, index.mapping()));
4948
}
5049

51-
public EsRelation(Source source, EsIndex index, List<Attribute> attributes, IndexMode indexMode, boolean frozen) {
50+
public EsRelation(
51+
Source source,
52+
String indexPattern,
53+
IndexMode indexMode,
54+
Map<String, IndexMode> indexNameWithModes,
55+
List<Attribute> attributes
56+
) {
5257
super(source);
53-
this.index = index;
54-
this.attrs = attributes;
58+
this.indexPattern = indexPattern;
5559
this.indexMode = indexMode;
56-
this.frozen = frozen;
60+
this.indexNameWithModes = indexNameWithModes;
61+
this.attrs = attributes;
5762
}
5863

5964
private static EsRelation readFrom(StreamInput in) throws IOException {
6065
Source source = Source.readFrom((PlanStreamInput) in);
61-
EsIndex esIndex = EsIndex.readFrom(in);
66+
String indexPattern;
67+
Map<String, IndexMode> indexNameWithModes;
68+
if (in.getTransportVersion().onOrAfter(ESQL_SKIP_ES_INDEX_SERIALIZATION)) {
69+
indexPattern = in.readString();
70+
indexNameWithModes = in.readMap(IndexMode::readFrom);
71+
} else {
72+
var index = EsIndex.readFrom(in);
73+
indexPattern = index.name();
74+
indexNameWithModes = index.indexNameWithModes();
75+
}
6276
List<Attribute> attributes = in.readNamedWriteableCollectionAsList(Attribute.class);
6377
if (supportingEsSourceOptions(in.getTransportVersion())) {
6478
// We don't do anything with these strings
@@ -67,23 +81,32 @@ private static EsRelation readFrom(StreamInput in) throws IOException {
6781
in.readOptionalString();
6882
}
6983
IndexMode indexMode = readIndexMode(in);
70-
boolean frozen = in.readBoolean();
71-
return new EsRelation(source, esIndex, attributes, indexMode, frozen);
84+
if (in.getTransportVersion().before(ESQL_SKIP_ES_INDEX_SERIALIZATION)) {
85+
in.readBoolean();
86+
}
87+
return new EsRelation(source, indexPattern, indexMode, indexNameWithModes, attributes);
7288
}
7389

7490
@Override
7591
public void writeTo(StreamOutput out) throws IOException {
7692
Source.EMPTY.writeTo(out);
77-
index().writeTo(out);
78-
out.writeNamedWriteableCollection(output());
93+
if (out.getTransportVersion().onOrAfter(ESQL_SKIP_ES_INDEX_SERIALIZATION)) {
94+
out.writeString(indexPattern);
95+
out.writeMap(indexNameWithModes, (o, v) -> IndexMode.writeTo(v, out));
96+
} else {
97+
new EsIndex(indexPattern, Map.of(), indexNameWithModes).writeTo(out);
98+
}
99+
out.writeNamedWriteableCollection(attrs);
79100
if (supportingEsSourceOptions(out.getTransportVersion())) {
80101
// write (null) string fillers expected by remote
81102
out.writeOptionalString(null);
82103
out.writeOptionalString(null);
83104
out.writeOptionalString(null);
84105
}
85-
writeIndexMode(out, indexMode());
86-
out.writeBoolean(frozen());
106+
writeIndexMode(out, indexMode);
107+
if (out.getTransportVersion().before(ESQL_SKIP_ES_INDEX_SERIALIZATION)) {
108+
out.writeBoolean(false);
109+
}
87110
}
88111

89112
private static boolean supportingEsSourceOptions(TransportVersion version) {
@@ -97,7 +120,7 @@ public String getWriteableName() {
97120

98121
@Override
99122
protected NodeInfo<EsRelation> info() {
100-
return NodeInfo.create(this, EsRelation::new, index, attrs, indexMode, frozen);
123+
return NodeInfo.create(this, EsRelation::new, indexPattern, indexMode, indexNameWithModes, attrs);
101124
}
102125

103126
private static List<Attribute> flatten(Source source, Map<String, EsField> mapping) {
@@ -128,23 +151,27 @@ private static List<Attribute> flatten(Source source, Map<String, EsField> mappi
128151
return list;
129152
}
130153

131-
public EsIndex index() {
132-
return index;
133-
}
134-
135-
public boolean frozen() {
136-
return frozen;
154+
public String indexPattern() {
155+
return indexPattern;
137156
}
138157

139158
public IndexMode indexMode() {
140159
return indexMode;
141160
}
142161

162+
public Map<String, IndexMode> indexNameWithModes() {
163+
return indexNameWithModes;
164+
}
165+
143166
@Override
144167
public List<Attribute> output() {
145168
return attrs;
146169
}
147170

171+
public Set<String> concreteIndices() {
172+
return indexNameWithModes.keySet();
173+
}
174+
148175
@Override
149176
public String commandName() {
150177
return "FROM";
@@ -159,7 +186,7 @@ public boolean expressionsResolved() {
159186

160187
@Override
161188
public int hashCode() {
162-
return Objects.hash(index, indexMode, frozen, attrs);
189+
return Objects.hash(indexPattern, indexMode, indexNameWithModes, attrs);
163190
}
164191

165192
@Override
@@ -173,17 +200,17 @@ public boolean equals(Object obj) {
173200
}
174201

175202
EsRelation other = (EsRelation) obj;
176-
return Objects.equals(index, other.index)
177-
&& indexMode == other.indexMode()
178-
&& frozen == other.frozen
203+
return Objects.equals(indexPattern, other.indexPattern)
204+
&& Objects.equals(indexMode, other.indexMode)
205+
&& Objects.equals(indexNameWithModes, other.indexNameWithModes)
179206
&& Objects.equals(attrs, other.attrs);
180207
}
181208

182209
@Override
183210
public String nodeString() {
184211
return nodeName()
185212
+ "["
186-
+ index
213+
+ indexPattern
187214
+ "]"
188215
+ (indexMode != IndexMode.STANDARD ? "[" + indexMode.name() + "]" : "")
189216
+ NodeUtils.limitedToString(attrs);

0 commit comments

Comments
 (0)