Skip to content

Commit 93f780c

Browse files
committed
Add force_synthetic_source to mget
This adds the option to force synthetic source to the MGET API. See elastic#87068 for more discussion on why you'd want to do that - the short version is to get an upper bound on the performance cost of using synthetic source in MGET.
1 parent a37edb7 commit 93f780c

File tree

9 files changed

+271
-26
lines changed

9 files changed

+271
-26
lines changed

rest-api-spec/src/main/resources/rest-api-spec/api/mget.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@
3535
]
3636
},
3737
"params":{
38+
"force_synthetic_source": {
39+
"type": "boolean",
40+
"description": "Should this request force synthetic _source? Use this to test if the mapping supports synthetic _source and to get a sense of the worst case performance. Fetches with this enabled will be slower the enabling synthetic source natively in the index.",
41+
"visibility": "feature_flag",
42+
"feature_flag": "es.index_mode_feature_flag_registered"
43+
},
3844
"stored_fields":{
3945
"type":"list",
4046
"description":"A comma-separated list of stored fields to return in the response"

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/mget/90_synthetic_source.yml

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,117 @@ keyword:
4545
- match:
4646
docs.1._source:
4747
kwd: bar
48+
49+
---
50+
force_synthetic_source_ok:
51+
- skip:
52+
version: " - 8.3.99"
53+
reason: introduced in 8.4.0
54+
55+
- do:
56+
indices.create:
57+
index: test
58+
body:
59+
mappings:
60+
_source:
61+
synthetic: false
62+
properties:
63+
kwd:
64+
type: keyword
65+
66+
- do:
67+
index:
68+
index: test
69+
id: 1
70+
body:
71+
kwd: foo
72+
73+
- do:
74+
index:
75+
index: test
76+
id: 2
77+
body:
78+
kwd: bar
79+
80+
# When _source is used in the fetch the original _source is perfect
81+
- do:
82+
mget:
83+
index: test
84+
body:
85+
ids: [1, 2]
86+
- match:
87+
docs.0._source:
88+
kwd: foo
89+
- match:
90+
docs.1._source:
91+
kwd: bar
92+
93+
# When we force synthetic source dots in field names get turned into objects
94+
- do:
95+
mget:
96+
index: test
97+
force_synthetic_source: true
98+
body:
99+
ids: [ 1, 2 ]
100+
- match:
101+
docs.0._source:
102+
kwd: foo
103+
- match:
104+
docs.1._source:
105+
kwd: bar
106+
107+
---
108+
force_synthetic_source_bad_mapping:
109+
- skip:
110+
version: " - 8.3.99"
111+
reason: introduced in 8.4.0
112+
113+
- do:
114+
indices.create:
115+
index: test
116+
body:
117+
settings:
118+
number_of_shards: 1 # Use a single shard to get consistent error messages
119+
mappings:
120+
_source:
121+
synthetic: false
122+
properties:
123+
text:
124+
type: text
125+
126+
- do:
127+
index:
128+
index: test
129+
id: 1
130+
body:
131+
text: foo
132+
133+
- do:
134+
index:
135+
index: test
136+
id: 2
137+
body:
138+
text: bar
139+
140+
# When _source is used in the fetch the original _source is perfect
141+
- do:
142+
mget:
143+
index: test
144+
body:
145+
ids: [ 1, 2 ]
146+
- match:
147+
docs.0._source:
148+
text: foo
149+
- match:
150+
docs.1._source:
151+
text: bar
152+
153+
# Forcing synthetic source fails because the mapping is invalid
154+
- do:
155+
mget:
156+
index: test
157+
force_synthetic_source: true
158+
body:
159+
ids: [ 1, 2 ]
160+
- match: {docs.0.error.reason: "field [text] of type [text] doesn't support synthetic source unless it has a sub-field of type [keyword] with doc values enabled and without ignore_above or a normalizer"}
161+
- match: {docs.1.error.reason: "field [text] of type [text] doesn't support synthetic source unless it has a sub-field of type [keyword] with doc values enabled and without ignore_above or a normalizer"}

server/src/main/java/org/elasticsearch/action/get/MultiGetRequest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.elasticsearch.core.RestApiVersion;
2929
import org.elasticsearch.index.VersionType;
3030
import org.elasticsearch.index.mapper.MapperService;
31+
import org.elasticsearch.index.mapper.SourceLoader;
3132
import org.elasticsearch.rest.action.document.RestMultiGetAction;
3233
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
3334
import org.elasticsearch.xcontent.ParseField;
@@ -246,6 +247,14 @@ public String toString() {
246247
boolean refresh;
247248
List<Item> items = new ArrayList<>();
248249

250+
/**
251+
* Should this request force {@link SourceLoader.Synthetic synthetic source}?
252+
* Use this to test if the mapping supports synthetic _source and to get a sense
253+
* of the worst case performance. Fetches with this enabled will be slower the
254+
* enabling synthetic source natively in the index.
255+
*/
256+
private boolean forceSyntheticSource = false;
257+
249258
public MultiGetRequest() {}
250259

251260
public MultiGetRequest(StreamInput in) throws IOException {
@@ -254,6 +263,11 @@ public MultiGetRequest(StreamInput in) throws IOException {
254263
refresh = in.readBoolean();
255264
realtime = in.readBoolean();
256265
items = in.readList(Item::new);
266+
if (in.getVersion().onOrAfter(Version.V_8_4_0)) {
267+
forceSyntheticSource = in.readBoolean();
268+
} else {
269+
forceSyntheticSource = false;
270+
}
257271
}
258272

259273
@Override
@@ -263,6 +277,13 @@ public void writeTo(StreamOutput out) throws IOException {
263277
out.writeBoolean(refresh);
264278
out.writeBoolean(realtime);
265279
out.writeList(items);
280+
if (out.getVersion().onOrAfter(Version.V_8_4_0)) {
281+
out.writeBoolean(forceSyntheticSource);
282+
} else {
283+
if (forceSyntheticSource) {
284+
throw new IllegalArgumentException("force_synthetic_source is not supported before 8.4.0");
285+
}
286+
}
266287
}
267288

268289
public List<Item> getItems() {
@@ -331,6 +352,26 @@ public MultiGetRequest refresh(boolean refresh) {
331352
return this;
332353
}
333354

355+
/**
356+
* Should this request force {@link SourceLoader.Synthetic synthetic source}?
357+
* Use this to test if the mapping supports synthetic _source and to get a sense
358+
* of the worst case performance. Fetches with this enabled will be slower the
359+
* enabling synthetic source natively in the index.
360+
*/
361+
public void setForceSyntheticSource(boolean forceSyntheticSource) {
362+
this.forceSyntheticSource = forceSyntheticSource;
363+
}
364+
365+
/**
366+
* Should this request force {@link SourceLoader.Synthetic synthetic source}?
367+
* Use this to test if the mapping supports synthetic _source and to get a sense
368+
* of the worst case performance. Fetches with this enabled will be slower the
369+
* enabling synthetic source natively in the index.
370+
*/
371+
public boolean isForceSyntheticSource() {
372+
return forceSyntheticSource;
373+
}
374+
334375
public MultiGetRequest add(
335376
@Nullable String defaultIndex,
336377
@Nullable String[] defaultFields,

server/src/main/java/org/elasticsearch/action/get/MultiGetShardRequest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88

99
package org.elasticsearch.action.get;
1010

11+
import org.elasticsearch.Version;
1112
import org.elasticsearch.action.ActionRequestValidationException;
1213
import org.elasticsearch.action.support.single.shard.SingleShardRequest;
1314
import org.elasticsearch.common.io.stream.StreamInput;
1415
import org.elasticsearch.common.io.stream.StreamOutput;
16+
import org.elasticsearch.index.mapper.SourceLoader;
1517

1618
import java.io.IOException;
1719
import java.util.ArrayList;
@@ -27,6 +29,14 @@ public class MultiGetShardRequest extends SingleShardRequest<MultiGetShardReques
2729
List<Integer> locations;
2830
List<MultiGetRequest.Item> items;
2931

32+
/**
33+
* Should this request force {@link SourceLoader.Synthetic synthetic source}?
34+
* Use this to test if the mapping supports synthetic _source and to get a sense
35+
* of the worst case performance. Fetches with this enabled will be slower the
36+
* enabling synthetic source natively in the index.
37+
*/
38+
private final boolean forceSyntheticSource;
39+
3040
MultiGetShardRequest(MultiGetRequest multiGetRequest, String index, int shardId) {
3141
super(index);
3242
this.shardId = shardId;
@@ -35,6 +45,7 @@ public class MultiGetShardRequest extends SingleShardRequest<MultiGetShardReques
3545
preference = multiGetRequest.preference;
3646
realtime = multiGetRequest.realtime;
3747
refresh = multiGetRequest.refresh;
48+
forceSyntheticSource = multiGetRequest.isForceSyntheticSource();
3849
}
3950

4051
MultiGetShardRequest(StreamInput in) throws IOException {
@@ -51,6 +62,11 @@ public class MultiGetShardRequest extends SingleShardRequest<MultiGetShardReques
5162
preference = in.readOptionalString();
5263
refresh = in.readBoolean();
5364
realtime = in.readBoolean();
65+
if (in.getVersion().onOrAfter(Version.V_8_4_0)) {
66+
forceSyntheticSource = in.readBoolean();
67+
} else {
68+
forceSyntheticSource = false;
69+
}
5470
}
5571

5672
@Override
@@ -66,6 +82,13 @@ public void writeTo(StreamOutput out) throws IOException {
6682
out.writeOptionalString(preference);
6783
out.writeBoolean(refresh);
6884
out.writeBoolean(realtime);
85+
if (out.getVersion().onOrAfter(Version.V_8_4_0)) {
86+
out.writeBoolean(forceSyntheticSource);
87+
} else {
88+
if (forceSyntheticSource) {
89+
throw new IllegalArgumentException("force_synthetic_source is not supported before 8.4.0");
90+
}
91+
}
6992
}
7093

7194
@Override
@@ -109,6 +132,16 @@ public MultiGetShardRequest refresh(boolean refresh) {
109132
return this;
110133
}
111134

135+
/**
136+
* Should this request force {@link SourceLoader.Synthetic synthetic source}?
137+
* Use this to test if the mapping supports synthetic _source and to get a sense
138+
* of the worst case performance. Fetches with this enabled will be slower the
139+
* enabling synthetic source natively in the index.
140+
*/
141+
public boolean isForceSyntheticSource() {
142+
return forceSyntheticSource;
143+
}
144+
112145
void add(int location, MultiGetRequest.Item item) {
113146
this.locations.add(location);
114147
this.items.add(item);

server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ protected MultiGetShardResponse shardOperation(MultiGetShardRequest request, Sha
124124
item.version(),
125125
item.versionType(),
126126
item.fetchSourceContext(),
127-
false
127+
request.isForceSyntheticSource()
128128
);
129129
response.add(request.locations.get(i), new GetResponse(getResult));
130130
} catch (RuntimeException e) {

server/src/main/java/org/elasticsearch/rest/action/document/RestMultiGetAction.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.elasticsearch.common.Strings;
1414
import org.elasticsearch.common.settings.Settings;
1515
import org.elasticsearch.core.RestApiVersion;
16+
import org.elasticsearch.index.IndexSettings;
1617
import org.elasticsearch.rest.BaseRestHandler;
1718
import org.elasticsearch.rest.RestRequest;
1819
import org.elasticsearch.rest.action.RestToXContentListener;
@@ -60,6 +61,10 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC
6061
multiGetRequest.refresh(request.paramAsBoolean("refresh", multiGetRequest.refresh()));
6162
multiGetRequest.preference(request.param("preference"));
6263
multiGetRequest.realtime(request.paramAsBoolean("realtime", multiGetRequest.realtime()));
64+
if (IndexSettings.isTimeSeriesModeEnabled() && request.paramAsBoolean("force_synthetic_source", false)) {
65+
multiGetRequest.setForceSyntheticSource(true);
66+
}
67+
6368
if (request.param("fields") != null) {
6469
throw new IllegalArgumentException(
6570
"The parameter [fields] is no longer supported, "

server/src/test/java/org/elasticsearch/action/get/GetRequestTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
*/
88
package org.elasticsearch.action.get;
99

10+
import org.elasticsearch.Version;
1011
import org.elasticsearch.action.ActionRequestValidationException;
12+
import org.elasticsearch.common.io.stream.BytesStreamOutput;
13+
import org.elasticsearch.common.io.stream.StreamOutput;
1114
import org.elasticsearch.test.ESTestCase;
1215

1316
import static org.hamcrest.CoreMatchers.hasItems;
@@ -33,4 +36,13 @@ public void testValidation() {
3336
assertThat(validate.validationErrors(), hasItems("id is missing"));
3437
}
3538
}
39+
40+
public void testForceSyntheticUnsupported() {
41+
GetRequest request = new GetRequest("index", "id");
42+
request.setForceSyntheticSource(true);
43+
StreamOutput out = new BytesStreamOutput();
44+
out.setVersion(Version.V_8_3_0);
45+
Exception e = expectThrows(IllegalArgumentException.class, () -> request.writeTo(out));
46+
assertEquals(e.getMessage(), "force_synthetic_source is not supported before 8.4.0");
47+
}
3648
}

server/src/test/java/org/elasticsearch/action/get/MultiGetRequestTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88

99
package org.elasticsearch.action.get;
1010

11+
import org.elasticsearch.Version;
1112
import org.elasticsearch.common.ParsingException;
1213
import org.elasticsearch.common.bytes.BytesReference;
14+
import org.elasticsearch.common.io.stream.BytesStreamOutput;
15+
import org.elasticsearch.common.io.stream.StreamOutput;
1316
import org.elasticsearch.index.VersionType;
1417
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
1518
import org.elasticsearch.test.ESTestCase;
@@ -117,6 +120,15 @@ public void testXContentSerialization() throws IOException {
117120
}
118121
}
119122

123+
public void testForceSyntheticUnsupported() {
124+
MultiGetRequest request = createTestInstance();
125+
request.setForceSyntheticSource(true);
126+
StreamOutput out = new BytesStreamOutput();
127+
out.setVersion(Version.V_8_3_0);
128+
Exception e = expectThrows(IllegalArgumentException.class, () -> request.writeTo(out));
129+
assertEquals(e.getMessage(), "force_synthetic_source is not supported before 8.4.0");
130+
}
131+
120132
private MultiGetRequest createTestInstance() {
121133
int numItems = randomIntBetween(0, 128);
122134
MultiGetRequest request = new MultiGetRequest();
@@ -149,6 +161,9 @@ private MultiGetRequest createTestInstance() {
149161
}
150162
request.add(item);
151163
}
164+
if (randomBoolean()) {
165+
request.setForceSyntheticSource(true);
166+
}
152167
return request;
153168
}
154169

0 commit comments

Comments
 (0)