Skip to content

Commit 361adcf

Browse files
yanjunhjpountz
authored andcommitted
Add limit to total number of fields in mapping. #17357
This is to prevent mapping explosion when dynamic keys such as UUID are used as field names. index.mapping.total_fields.limit specifies the total number of fields an index can have. An exception will be thrown when the limit is reached. The default limit is 1000. Value 0 means no limit. This setting is runtime adjustable Closes #11443
1 parent c356b30 commit 361adcf

File tree

5 files changed

+48
-2
lines changed

5 files changed

+48
-2
lines changed

core/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ public final class IndexScopedSettings extends AbstractScopedSettings {
128128
PercolatorQueryCache.INDEX_MAP_UNMAPPED_FIELDS_AS_STRING_SETTING,
129129
MapperService.INDEX_MAPPER_DYNAMIC_SETTING,
130130
MapperService.INDEX_MAPPING_NESTED_FIELDS_LIMIT_SETTING,
131+
MapperService.INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING,
131132
BitsetFilterCache.INDEX_LOAD_RANDOM_ACCESS_FILTERS_EAGERLY_SETTING,
132133
IndexModule.INDEX_STORE_TYPE_SETTING,
133134
IndexModule.INDEX_QUERY_CACHE_TYPE_SETTING,

core/src/main/java/org/elasticsearch/index/mapper/MapperService.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ public enum MergeReason {
8484
public static final String DEFAULT_MAPPING = "_default_";
8585
public static final Setting<Long> INDEX_MAPPING_NESTED_FIELDS_LIMIT_SETTING =
8686
Setting.longSetting("index.mapping.nested_fields.limit", 50L, 0, Property.Dynamic, Property.IndexScope);
87+
public static final Setting<Long> INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING =
88+
Setting.longSetting("index.mapping.total_fields.limit", 1000L, 0, Property.Dynamic, Property.IndexScope);
8789
public static final boolean INDEX_MAPPER_DYNAMIC_DEFAULT = true;
8890
public static final Setting<Boolean> INDEX_MAPPER_DYNAMIC_SETTING =
8991
Setting.boolSetting("index.mapper.dynamic", INDEX_MAPPER_DYNAMIC_DEFAULT, Property.IndexScope);
@@ -289,6 +291,7 @@ private synchronized DocumentMapper merge(DocumentMapper mapper, MergeReason rea
289291
// deserializing cluster state that was sent by the master node,
290292
// this check will be skipped.
291293
checkNestedFieldsLimit(fullPathObjectMappers);
294+
checkTotalFieldsLimit(objectMappers.size() + fieldMappers.size());
292295
}
293296

294297
Set<String> parentTypes = this.parentTypes;
@@ -403,11 +406,18 @@ private void checkNestedFieldsLimit(Map<String, ObjectMapper> fullPathObjectMapp
403406
actualNestedFields++;
404407
}
405408
}
406-
if (allowedNestedFields >= 0 && actualNestedFields > allowedNestedFields) {
409+
if (actualNestedFields > allowedNestedFields) {
407410
throw new IllegalArgumentException("Limit of nested fields [" + allowedNestedFields + "] in index [" + index().getName() + "] has been exceeded");
408411
}
409412
}
410413

414+
private void checkTotalFieldsLimit(long totalMappers) {
415+
long allowedTotalFields = indexSettings.getValue(INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING);
416+
if (allowedTotalFields < totalMappers) {
417+
throw new IllegalArgumentException("Limit of total fields [" + allowedTotalFields + "] in index [" + index().getName() + "] has been exceeded");
418+
}
419+
}
420+
411421
public DocumentMapper parse(String mappingType, CompressedXContent mappingSource, boolean applyDefault) throws MapperParsingException {
412422
String defaultMappingSource;
413423
if (PercolatorFieldMapper.TYPE_NAME.equals(mappingType)) {

core/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.elasticsearch.common.xcontent.XContentBuilder;
3232
import org.elasticsearch.common.xcontent.XContentFactory;
3333
import org.elasticsearch.index.IndexNotFoundException;
34+
import org.elasticsearch.index.mapper.MapperService;
3435
import org.elasticsearch.test.ESIntegTestCase;
3536
import org.elasticsearch.test.hamcrest.CollectionAssertions;
3637
import org.junit.Before;
@@ -145,7 +146,9 @@ public void testLargeClusterStatePublishing() throws Exception {
145146
int numberOfShards = scaledRandomIntBetween(1, cluster().numDataNodes());
146147
// if the create index is ack'ed, then all nodes have successfully processed the cluster state
147148
assertAcked(client().admin().indices().prepareCreate("test")
148-
.setSettings(IndexMetaData.SETTING_NUMBER_OF_SHARDS, numberOfShards, IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
149+
.setSettings(IndexMetaData.SETTING_NUMBER_OF_SHARDS, numberOfShards,
150+
IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0,
151+
MapperService.INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING.getKey(), Long.MAX_VALUE)
149152
.addMapping("type", mapping)
150153
.setTimeout("60s").get());
151154
ensureGreen(); // wait for green state, so its both green, and there are no more pending events

core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,21 @@
3030
import org.elasticsearch.ExceptionsHelper;
3131
import org.elasticsearch.common.compress.CompressedXContent;
3232
import org.elasticsearch.common.lucene.search.Queries;
33+
import org.elasticsearch.common.settings.Settings;
34+
import org.elasticsearch.common.xcontent.XContentFactory;
3335
import org.elasticsearch.index.IndexService;
36+
import org.elasticsearch.index.mapper.MapperService.MergeReason;
3437
import org.elasticsearch.index.mapper.internal.TypeFieldMapper;
3538
import org.elasticsearch.test.ESSingleNodeTestCase;
3639
import org.junit.Rule;
3740
import org.junit.rules.ExpectedException;
3841

42+
import java.io.IOException;
43+
import java.io.UncheckedIOException;
3944
import java.util.Arrays;
4045
import java.util.Collections;
4146
import java.util.HashSet;
47+
import java.util.function.Function;
4248
import java.util.concurrent.ExecutionException;
4349

4450
import static org.hamcrest.CoreMatchers.containsString;
@@ -135,4 +141,24 @@ public void testIndexIntoDefaultMapping() throws Throwable {
135141
assertFalse(indexService.mapperService().hasMapping(MapperService.DEFAULT_MAPPING));
136142
}
137143

144+
public void testTotalFieldsExceedsLimit() throws Throwable {
145+
Function<String, String> mapping = type -> {
146+
try {
147+
return XContentFactory.jsonBuilder().startObject().startObject(type).startObject("properties")
148+
.startObject("field1").field("type", "string")
149+
.endObject().endObject().endObject().endObject().string();
150+
} catch (IOException e) {
151+
throw new UncheckedIOException(e);
152+
}
153+
};
154+
createIndex("test1").mapperService().merge("type", new CompressedXContent(mapping.apply("type")), MergeReason.MAPPING_UPDATE, false);
155+
//set total number of fields to 1 to trigger an exception
156+
try {
157+
createIndex("test2", Settings.builder().put(MapperService.INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING.getKey(), 1).build())
158+
.mapperService().merge("type", new CompressedXContent(mapping.apply("type")), MergeReason.MAPPING_UPDATE, false);
159+
fail("Expected IllegalArgumentException");
160+
} catch (IllegalArgumentException e) {
161+
assertThat(e.getMessage(), containsString("Limit of total fields [1] in index [test2] has been exceeded"));
162+
}
163+
}
138164
}

docs/reference/mapping/dynamic/field-mapping.asciidoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ detected. All other datatypes must be mapped explicitly.
3030
Besides the options listed below, dynamic field mapping rules can be further
3131
customised with <<dynamic-templates,`dynamic_templates`>>.
3232

33+
[[total-fields-limit]]
34+
==== Total fields limit
35+
36+
To avoid mapping explosion, Index has a default limit of 1000 total number of fields.
37+
The default setting can be updated with `index.mapping.total_fields.limit`.
38+
3339
[[date-detection]]
3440
==== Date detection
3541

0 commit comments

Comments
 (0)