Skip to content

Commit 6a52675

Browse files
Use synthetic recovery source by default if synthetic source is enabled (#119110)
We experimented with using synthetic source for recovery and observed quite positive impact on indexing throughput by means of our nightly Rally benchmarks. As a result, here we enable it by default when synthetic source is used. To be more precise, if `index.mapping.source.mode` setting is `synthetic` we enable recovery source by means of synthetic source. Moreover, enabling synthetic source recovery is done behind a feature flag. That would allow us to enable it in snapshot builds which in turn will allow us to see performance results in Rally nightly benchmarks.
1 parent 09bc343 commit 6a52675

File tree

12 files changed

+382
-29
lines changed

12 files changed

+382
-29
lines changed

muted-tests.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ tests:
134134
- class: org.elasticsearch.datastreams.DataStreamsClientYamlTestSuiteIT
135135
method: test {p0=data_stream/120_data_streams_stats/Multiple data stream}
136136
issue: https://github.com/elastic/elasticsearch/issues/118217
137+
# TODO: re-enable after backporting https://github.com/elastic/elasticsearch/pull/119110
138+
- class: org.elasticsearch.test.rest.ClientYamlTestSuiteIT
139+
method: test {yaml=update/100_synthetic_source/keyword}
140+
# TODO: re-enable after backporting https://github.com/elastic/elasticsearch/pull/119110
141+
- class: org.elasticsearch.test.rest.ClientYamlTestSuiteIT
142+
method: test {yaml=update/100_synthetic_source/stored text}
137143
- class: org.elasticsearch.xpack.searchablesnapshots.RetrySearchIntegTests
138144
method: testSearcherId
139145
issue: https://github.com/elastic/elasticsearch/issues/118374

qa/smoke-test-multinode/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,7 @@ tasks.named("yamlRestTest").configure {
2828
'cat.templates/10_basic/No templates',
2929
'cat.templates/10_basic/Sort templates',
3030
'cat.templates/10_basic/Multiple template',
31+
'update/100_synthetic_source/keyword',
32+
'update/100_synthetic_source/stored text'
3133
].join(',')
3234
}

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/update/100_synthetic_source.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ setup:
66
---
77
keyword:
88
- requires:
9-
cluster_features: ["gte_v8.4.0"]
10-
reason: introduced in 8.4.0
9+
cluster_features: [ "mapper.synthetic_recovery_source" ]
10+
reason: requires synthetic recovery source
1111

1212
- do:
1313
indices.create:
@@ -60,13 +60,14 @@ keyword:
6060
index: test
6161
run_expensive_tasks: true
6262
- is_false: test.fields._source
63-
- is_true: test.fields._recovery_source
63+
# When synthetic source is used there is no _recovery_source field
64+
- match: { test.fields._recovery_source: null }
6465

6566
---
6667
stored text:
6768
- requires:
68-
cluster_features: ["gte_v8.5.0"]
69-
reason: introduced in 8.5.0
69+
cluster_features: [ "mapper.synthetic_recovery_source" ]
70+
reason: requires synthetic recovery source
7071

7172
- do:
7273
indices.create:
@@ -121,4 +122,5 @@ stored text:
121122
index: test
122123
run_expensive_tasks: true
123124
- is_false: test.fields._source
124-
- is_true: test.fields._recovery_source
125+
# When synthetic source is used there is no _recovery_source field
126+
- match: { test.fields._recovery_source: null }

server/src/main/java/org/elasticsearch/common/settings/Setting.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,15 @@ public static Setting<Boolean> boolSetting(String key, boolean defaultValue, Val
15801580
return new Setting<>(key, Boolean.toString(defaultValue), booleanParser(key, properties), validator, properties);
15811581
}
15821582

1583+
public static Setting<Boolean> boolSetting(
1584+
String key,
1585+
Function<Settings, String> defaultValueFn,
1586+
Validator<Boolean> validator,
1587+
Property... properties
1588+
) {
1589+
return new Setting<>(key, defaultValueFn, booleanParser(key, properties), validator, properties);
1590+
}
1591+
15831592
public static Setting<Boolean> boolSetting(String key, Function<Settings, String> defaultValueFn, Property... properties) {
15841593
return new Setting<>(key, defaultValueFn, booleanParser(key, properties), properties);
15851594
}

server/src/main/java/org/elasticsearch/index/IndexSettings.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.elasticsearch.common.time.DateUtils;
2525
import org.elasticsearch.common.unit.ByteSizeUnit;
2626
import org.elasticsearch.common.unit.ByteSizeValue;
27+
import org.elasticsearch.common.util.FeatureFlag;
2728
import org.elasticsearch.core.TimeValue;
2829
import org.elasticsearch.index.mapper.IgnoredSourceFieldMapper;
2930
import org.elasticsearch.index.mapper.Mapper;
@@ -39,6 +40,7 @@
3940
import java.util.List;
4041
import java.util.Locale;
4142
import java.util.Map;
43+
import java.util.Objects;
4244
import java.util.concurrent.TimeUnit;
4345
import java.util.function.Consumer;
4446

@@ -722,9 +724,25 @@ public Iterator<Setting<?>> settings() {
722724
Setting.Property.ServerlessPublic
723725
);
724726

727+
public static final FeatureFlag RECOVERY_USE_SYNTHETIC_SOURCE = new FeatureFlag("index_recovery_use_synthetic_source");
725728
public static final Setting<Boolean> RECOVERY_USE_SYNTHETIC_SOURCE_SETTING = Setting.boolSetting(
726729
"index.recovery.use_synthetic_source",
727-
false,
730+
settings -> {
731+
boolean isSyntheticSourceRecoveryFeatureFlagEnabled = RECOVERY_USE_SYNTHETIC_SOURCE.isEnabled();
732+
boolean isNewIndexVersion = SETTING_INDEX_VERSION_CREATED.get(settings)
733+
.onOrAfter(IndexVersions.USE_SYNTHETIC_SOURCE_FOR_RECOVERY_BY_DEFAULT);
734+
boolean isIndexVersionInBackportRange = SETTING_INDEX_VERSION_CREATED.get(settings)
735+
.between(IndexVersions.USE_SYNTHETIC_SOURCE_FOR_RECOVERY_BY_DEFAULT_BACKPORT, IndexVersions.UPGRADE_TO_LUCENE_10_0_0);
736+
737+
boolean useSyntheticRecoverySource = isSyntheticSourceRecoveryFeatureFlagEnabled
738+
&& (isNewIndexVersion || isIndexVersionInBackportRange);
739+
740+
return String.valueOf(
741+
useSyntheticRecoverySource
742+
&& Objects.equals(INDEX_MAPPER_SOURCE_MODE_SETTING.get(settings), SourceFieldMapper.Mode.SYNTHETIC)
743+
);
744+
745+
},
728746
new Setting.Validator<>() {
729747
@Override
730748
public void validate(Boolean value) {}
@@ -1083,7 +1101,8 @@ public IndexSettings(final IndexMetadata indexMetadata, final Settings nodeSetti
10831101
skipIgnoredSourceRead = scopedSettings.get(IgnoredSourceFieldMapper.SKIP_IGNORED_SOURCE_READ_SETTING);
10841102
indexMappingSourceMode = scopedSettings.get(INDEX_MAPPER_SOURCE_MODE_SETTING);
10851103
recoverySourceEnabled = RecoverySettings.INDICES_RECOVERY_SOURCE_ENABLED_SETTING.get(nodeSettings);
1086-
recoverySourceSyntheticEnabled = scopedSettings.get(RECOVERY_USE_SYNTHETIC_SOURCE_SETTING);
1104+
recoverySourceSyntheticEnabled = DiscoveryNode.isStateless(nodeSettings) == false
1105+
&& scopedSettings.get(RECOVERY_USE_SYNTHETIC_SOURCE_SETTING);
10871106
if (recoverySourceSyntheticEnabled) {
10881107
if (DiscoveryNode.isStateless(settings)) {
10891108
throw new IllegalArgumentException("synthetic recovery source is only allowed in stateful");

server/src/main/java/org/elasticsearch/index/IndexVersions.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ private static Version parseUnchecked(String version) {
134134
public static final IndexVersion UPGRADE_TO_LUCENE_9_12_1 = def(8_523_0_00, parseUnchecked("9.12.1"));
135135
public static final IndexVersion INFERENCE_METADATA_FIELDS_BACKPORT = def(8_524_0_00, parseUnchecked("9.12.1"));
136136
public static final IndexVersion LOGSB_OPTIONAL_SORTING_ON_HOST_NAME_BACKPORT = def(8_525_0_00, parseUnchecked("9.12.1"));
137+
public static final IndexVersion USE_SYNTHETIC_SOURCE_FOR_RECOVERY_BY_DEFAULT_BACKPORT = def(8_526_0_00, parseUnchecked("9.12.1"));
137138
public static final IndexVersion UPGRADE_TO_LUCENE_10_0_0 = def(9_000_0_00, Version.LUCENE_10_0_0);
138139
public static final IndexVersion LOGSDB_DEFAULT_IGNORE_DYNAMIC_BEYOND_LIMIT = def(9_001_0_00, Version.LUCENE_10_0_0);
139140
public static final IndexVersion TIME_BASED_K_ORDERED_DOC_ID = def(9_002_0_00, Version.LUCENE_10_0_0);
@@ -144,6 +145,7 @@ private static Version parseUnchecked(String version) {
144145
public static final IndexVersion SOURCE_MAPPER_MODE_ATTRIBUTE_NOOP = def(9_007_0_00, Version.LUCENE_10_0_0);
145146
public static final IndexVersion HOSTNAME_DOC_VALUES_SPARSE_INDEX = def(9_008_0_00, Version.LUCENE_10_0_0);
146147
public static final IndexVersion UPGRADE_TO_LUCENE_10_1_0 = def(9_009_0_00, Version.LUCENE_10_1_0);
148+
public static final IndexVersion USE_SYNTHETIC_SOURCE_FOR_RECOVERY_BY_DEFAULT = def(9_010_00_0, Version.LUCENE_10_1_0);
147149
/*
148150
* STOP! READ THIS FIRST! No, really,
149151
* ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _

server/src/test/java/org/elasticsearch/index/mapper/IgnoredSourceFieldMapperTests.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2427,8 +2427,14 @@ protected void validateRoundTripReader(String syntheticSource, DirectoryReader r
24272427
// and since the copy is exact, contents of ignored source are different.
24282428
assertReaderEquals(
24292429
"round trip " + syntheticSource,
2430-
new FieldMaskingReader(Set.of(SourceFieldMapper.RECOVERY_SOURCE_NAME, IgnoredSourceFieldMapper.NAME), reader),
2431-
new FieldMaskingReader(Set.of(SourceFieldMapper.RECOVERY_SOURCE_NAME, IgnoredSourceFieldMapper.NAME), roundTripReader)
2430+
new FieldMaskingReader(
2431+
Set.of(SourceFieldMapper.RECOVERY_SOURCE_NAME, IgnoredSourceFieldMapper.NAME, SourceFieldMapper.RECOVERY_SOURCE_SIZE_NAME),
2432+
reader
2433+
),
2434+
new FieldMaskingReader(
2435+
Set.of(SourceFieldMapper.RECOVERY_SOURCE_NAME, IgnoredSourceFieldMapper.NAME, SourceFieldMapper.RECOVERY_SOURCE_SIZE_NAME),
2436+
roundTripReader
2437+
)
24322438
);
24332439
}
24342440
}

server/src/test/java/org/elasticsearch/index/mapper/SourceFieldMapperTests.java

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,13 @@ public void testRecoverySourceWithSyntheticSource() throws IOException {
489489
MapperService mapperService = createMapperService(settings, topMapping(b -> {}));
490490
DocumentMapper docMapper = mapperService.documentMapper();
491491
ParsedDocument doc = docMapper.parse(source(b -> b.field("field1", "value1")));
492-
assertNotNull(doc.rootDoc().getField("_recovery_source"));
493-
assertThat(doc.rootDoc().getField("_recovery_source").binaryValue(), equalTo(new BytesRef("{\"field1\":\"value1\"}")));
492+
if (IndexSettings.RECOVERY_USE_SYNTHETIC_SOURCE.isEnabled() == false) {
493+
// TODO: remove this if branch when removing the 'index_recovery_use_synthetic_source' feature flag
494+
assertNotNull(doc.rootDoc().getField("_recovery_source"));
495+
assertThat(doc.rootDoc().getField("_recovery_source").binaryValue(), equalTo(new BytesRef("{\"field1\":\"value1\"}")));
496+
} else {
497+
assertNull(doc.rootDoc().getField("_recovery_source"));
498+
}
494499
}
495500
{
496501
Settings settings = Settings.builder()
@@ -521,8 +526,16 @@ public void testRecoverySourceWithLogs() throws IOException {
521526
MapperService mapperService = createMapperService(settings, mapping(b -> {}));
522527
DocumentMapper docMapper = mapperService.documentMapper();
523528
ParsedDocument doc = docMapper.parse(source(b -> { b.field("@timestamp", "2012-02-13"); }));
524-
assertNotNull(doc.rootDoc().getField("_recovery_source"));
525-
assertThat(doc.rootDoc().getField("_recovery_source").binaryValue(), equalTo(new BytesRef("{\"@timestamp\":\"2012-02-13\"}")));
529+
if (IndexSettings.RECOVERY_USE_SYNTHETIC_SOURCE.isEnabled() == false) {
530+
// TODO: remove this if branch when removing the 'index_recovery_use_synthetic_source' feature flag
531+
assertNotNull(doc.rootDoc().getField("_recovery_source"));
532+
assertThat(
533+
doc.rootDoc().getField("_recovery_source").binaryValue(),
534+
equalTo(new BytesRef("{\"@timestamp\":\"2012-02-13\"}"))
535+
);
536+
} else {
537+
assertNull(doc.rootDoc().getField("_recovery_source"));
538+
}
526539
}
527540
{
528541
Settings settings = Settings.builder()
@@ -715,8 +728,16 @@ public void testRecoverySourceWithLogsCustom() throws IOException {
715728
MapperService mapperService = createMapperService(settings, mappings);
716729
DocumentMapper docMapper = mapperService.documentMapper();
717730
ParsedDocument doc = docMapper.parse(source(b -> { b.field("@timestamp", "2012-02-13"); }));
718-
assertNotNull(doc.rootDoc().getField("_recovery_source"));
719-
assertThat(doc.rootDoc().getField("_recovery_source").binaryValue(), equalTo(new BytesRef("{\"@timestamp\":\"2012-02-13\"}")));
731+
if (IndexSettings.RECOVERY_USE_SYNTHETIC_SOURCE.isEnabled() == false) {
732+
// TODO: remove this if branch when removing the 'index_recovery_use_synthetic_source' feature flag
733+
assertNotNull(doc.rootDoc().getField("_recovery_source"));
734+
assertThat(
735+
doc.rootDoc().getField("_recovery_source").binaryValue(),
736+
equalTo(new BytesRef("{\"@timestamp\":\"2012-02-13\"}"))
737+
);
738+
} else {
739+
assertNull(doc.rootDoc().getField("_recovery_source"));
740+
}
720741
}
721742
{
722743
Settings settings = Settings.builder()
@@ -742,11 +763,16 @@ public void testRecoverySourceWithTimeSeries() throws IOException {
742763
}));
743764
DocumentMapper docMapper = mapperService.documentMapper();
744765
ParsedDocument doc = docMapper.parse(source("123", b -> b.field("@timestamp", "2012-02-13").field("field", "value1"), null));
745-
assertNotNull(doc.rootDoc().getField("_recovery_source"));
746-
assertThat(
747-
doc.rootDoc().getField("_recovery_source").binaryValue(),
748-
equalTo(new BytesRef("{\"@timestamp\":\"2012-02-13\",\"field\":\"value1\"}"))
749-
);
766+
if (IndexSettings.RECOVERY_USE_SYNTHETIC_SOURCE.isEnabled() == false) {
767+
// TODO: remove this if branch when removing the 'index_recovery_use_synthetic_source' feature flag
768+
assertNotNull(doc.rootDoc().getField("_recovery_source"));
769+
assertThat(
770+
doc.rootDoc().getField("_recovery_source").binaryValue(),
771+
equalTo(new BytesRef("{\"@timestamp\":\"2012-02-13\",\"field\":\"value1\"}"))
772+
);
773+
} else {
774+
assertNull(doc.rootDoc().getField("_recovery_source"));
775+
}
750776
}
751777
{
752778
Settings settings = Settings.builder()
@@ -790,11 +816,16 @@ public void testRecoverySourceWithTimeSeriesCustom() throws IOException {
790816
MapperService mapperService = createMapperService(settings, mappings);
791817
DocumentMapper docMapper = mapperService.documentMapper();
792818
ParsedDocument doc = docMapper.parse(source("123", b -> b.field("@timestamp", "2012-02-13").field("field", "value1"), null));
793-
assertNotNull(doc.rootDoc().getField("_recovery_source"));
794-
assertThat(
795-
doc.rootDoc().getField("_recovery_source").binaryValue(),
796-
equalTo(new BytesRef("{\"@timestamp\":\"2012-02-13\",\"field\":\"value1\"}"))
797-
);
819+
if (IndexSettings.RECOVERY_USE_SYNTHETIC_SOURCE.isEnabled() == false) {
820+
// TODO: remove this if branch when removing the 'index_recovery_use_synthetic_source' feature flag
821+
assertNotNull(doc.rootDoc().getField("_recovery_source"));
822+
assertThat(
823+
doc.rootDoc().getField("_recovery_source").binaryValue(),
824+
equalTo(new BytesRef("{\"@timestamp\":\"2012-02-13\",\"field\":\"value1\"}"))
825+
);
826+
} else {
827+
assertNull(doc.rootDoc().getField("_recovery_source"));
828+
}
798829
}
799830
{
800831
Settings settings = Settings.builder()

test/framework/src/main/java/org/elasticsearch/index/mapper/MapperServiceTestCase.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -884,8 +884,11 @@ protected void validateRoundTripReader(String syntheticSource, DirectoryReader r
884884
throws IOException {
885885
assertReaderEquals(
886886
"round trip " + syntheticSource,
887-
new FieldMaskingReader(SourceFieldMapper.RECOVERY_SOURCE_NAME, reader),
888-
new FieldMaskingReader(SourceFieldMapper.RECOVERY_SOURCE_NAME, roundTripReader)
887+
new FieldMaskingReader(Set.of(SourceFieldMapper.RECOVERY_SOURCE_NAME, SourceFieldMapper.RECOVERY_SOURCE_SIZE_NAME), reader),
888+
new FieldMaskingReader(
889+
Set.of(SourceFieldMapper.RECOVERY_SOURCE_NAME, SourceFieldMapper.RECOVERY_SOURCE_SIZE_NAME),
890+
roundTripReader
891+
)
889892
);
890893
}
891894

x-pack/plugin/logsdb/build.gradle

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,13 @@ tasks.named("javaRestTest").configure {
4242
tasks.named('yamlRestTest') {
4343
usesDefaultDistribution()
4444
}
45+
46+
tasks.named("yamlRestTest") {
47+
if (buildParams.isSnapshotBuild() == false) {
48+
systemProperty 'tests.rest.blacklist', [
49+
"60_synthetic_source_recovery/*"
50+
].join(',')
51+
}
52+
}
53+
54+

0 commit comments

Comments
 (0)