Store Template's mappings as bytes for disk serialization (#78746) (#79522)

This change the way we store mappings in `Template` during serialization
to disk - instead storing it as map we use byte array that we already
have. This avoids deserialization-serialization cycle during storing
cluster state on disk.
# Conflicts:
#	server/src/main/java/org/elasticsearch/cluster/coordination/ElasticsearchNodeCommand.java
This commit is contained in:
Przemko Robakowski 2021-10-20 01:14:54 +02:00 committed by GitHub
parent c993176e5f
commit 8fe8e6f039
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 42 additions and 19 deletions

View file

@ -13,6 +13,7 @@ import org.elasticsearch.cluster.metadata.ComponentTemplate;
import org.elasticsearch.cluster.metadata.Template; import org.elasticsearch.cluster.metadata.Template;
import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xcontent.ToXContent;
import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
@ -79,7 +80,7 @@ public class GetComponentTemplatesResponseTests extends ESTestCase {
builder.startObject(); builder.startObject();
builder.field("name", e.getKey()); builder.field("name", e.getKey());
builder.field("component_template"); builder.field("component_template");
e.getValue().toXContent(builder, null); e.getValue().toXContent(builder, ToXContent.EMPTY_PARAMS);
builder.endObject(); builder.endObject();
} }
builder.endArray(); builder.endArray();

View file

@ -10,6 +10,7 @@ package org.elasticsearch.client.indices;
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.ToXContent;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import java.io.IOException; import java.io.IOException;
@ -53,7 +54,7 @@ public class GetComposableIndexTemplatesResponseTests extends ESTestCase {
builder.startObject(); builder.startObject();
builder.field("name", e.getKey()); builder.field("name", e.getKey());
builder.field("index_template"); builder.field("index_template");
e.getValue().toXContent(builder, null); e.getValue().toXContent(builder, ToXContent.EMPTY_PARAMS);
builder.endObject(); builder.endObject();
} }
builder.endArray(); builder.endArray();

View file

@ -23,20 +23,22 @@ import org.elasticsearch.cluster.ClusterModule;
import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.Diff; import org.elasticsearch.cluster.Diff;
import org.elasticsearch.cluster.metadata.ComponentTemplateMetadata;
import org.elasticsearch.cluster.metadata.ComposableIndexTemplateMetadata;
import org.elasticsearch.cluster.metadata.DataStreamMetadata; import org.elasticsearch.cluster.metadata.DataStreamMetadata;
import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.core.Tuple;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentParser;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.env.NodeMetadata; import org.elasticsearch.env.NodeMetadata;
import org.elasticsearch.gateway.PersistedClusterStateService; import org.elasticsearch.gateway.PersistedClusterStateService;
import org.elasticsearch.xcontent.NamedXContentRegistry;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentParser;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -70,7 +72,8 @@ public abstract class ElasticsearchNodeCommand extends EnvironmentAwareCommand {
public <T, C> T parseNamedObject(Class<T> categoryClass, String name, XContentParser parser, C context) throws IOException { public <T, C> T parseNamedObject(Class<T> categoryClass, String name, XContentParser parser, C context) throws IOException {
// Currently, two unknown top-level objects are present // Currently, two unknown top-level objects are present
if (Metadata.Custom.class.isAssignableFrom(categoryClass)) { if (Metadata.Custom.class.isAssignableFrom(categoryClass)) {
if (DataStreamMetadata.TYPE.equals(name)) { if (DataStreamMetadata.TYPE.equals(name) || ComposableIndexTemplateMetadata.TYPE.equals(name)
|| ComponentTemplateMetadata.TYPE.equals(name)) {
// DataStreamMetadata is used inside Metadata class for validation purposes and building the indicesLookup, // DataStreamMetadata is used inside Metadata class for validation purposes and building the indicesLookup,
// therefor even es node commands need to be able to parse it. // therefor even es node commands need to be able to parse it.
return super.parseNamedObject(categoryClass, name, parser, context); return super.parseNamedObject(categoryClass, name, parser, context);

View file

@ -129,7 +129,7 @@ public class ComponentTemplate extends AbstractDiffable<ComponentTemplate> imple
@Override @Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(); builder.startObject();
builder.field(TEMPLATE.getPreferredName(), this.template); builder.field(TEMPLATE.getPreferredName(), this.template, params);
if (this.version != null) { if (this.version != null) {
builder.field(VERSION.getPreferredName(), this.version); builder.field(VERSION.getPreferredName(), this.version);
} }

View file

@ -98,7 +98,7 @@ public class ComponentTemplateMetadata implements Metadata.Custom {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(COMPONENT_TEMPLATE.getPreferredName()); builder.startObject(COMPONENT_TEMPLATE.getPreferredName());
for (Map.Entry<String, ComponentTemplate> template : componentTemplates.entrySet()) { for (Map.Entry<String, ComponentTemplate> template : componentTemplates.entrySet()) {
builder.field(template.getKey(), template.getValue()); builder.field(template.getKey(), template.getValue(), params);
} }
builder.endObject(); builder.endObject();
return builder; return builder;

View file

@ -218,7 +218,7 @@ public class ComposableIndexTemplate extends AbstractDiffable<ComposableIndexTem
builder.startObject(); builder.startObject();
builder.stringListField(INDEX_PATTERNS.getPreferredName(), this.indexPatterns); builder.stringListField(INDEX_PATTERNS.getPreferredName(), this.indexPatterns);
if (this.template != null) { if (this.template != null) {
builder.field(TEMPLATE.getPreferredName(), this.template); builder.field(TEMPLATE.getPreferredName(), this.template, params);
} }
if (this.componentTemplates != null) { if (this.componentTemplates != null) {
builder.stringListField(COMPOSED_OF.getPreferredName(), this.componentTemplates); builder.stringListField(COMPOSED_OF.getPreferredName(), this.componentTemplates);
@ -233,7 +233,7 @@ public class ComposableIndexTemplate extends AbstractDiffable<ComposableIndexTem
builder.field(METADATA.getPreferredName(), metadata); builder.field(METADATA.getPreferredName(), metadata);
} }
if (this.dataStreamTemplate != null) { if (this.dataStreamTemplate != null) {
builder.field(DATA_STREAM.getPreferredName(), dataStreamTemplate); builder.field(DATA_STREAM.getPreferredName(), dataStreamTemplate, params);
} }
if (this.allowAutoCreate != null) { if (this.allowAutoCreate != null) {
builder.field(ALLOW_AUTO_CREATE.getPreferredName(), allowAutoCreate); builder.field(ALLOW_AUTO_CREATE.getPreferredName(), allowAutoCreate);

View file

@ -99,7 +99,7 @@ public class ComposableIndexTemplateMetadata implements Metadata.Custom {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(INDEX_TEMPLATE.getPreferredName()); builder.startObject(INDEX_TEMPLATE.getPreferredName());
for (Map.Entry<String, ComposableIndexTemplate> template : indexTemplates.entrySet()) { for (Map.Entry<String, ComposableIndexTemplate> template : indexTemplates.entrySet()) {
builder.field(template.getKey(), template.getValue()); builder.field(template.getKey(), template.getValue(), params);
} }
builder.endObject(); builder.endObject();
return builder; return builder;

View file

@ -11,6 +11,7 @@ package org.elasticsearch.cluster.metadata;
import org.elasticsearch.cluster.AbstractDiffable; import org.elasticsearch.cluster.AbstractDiffable;
import org.elasticsearch.common.util.Maps; import org.elasticsearch.common.util.Maps;
import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Nullable;
import org.elasticsearch.xcontent.ObjectParser;
import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.ParseField;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.compress.CompressedXContent;
@ -27,6 +28,7 @@ import org.elasticsearch.xcontent.XContentType;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import java.io.IOException; import java.io.IOException;
import java.util.Base64;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -47,8 +49,18 @@ public class Template extends AbstractDiffable<Template> implements ToXContentOb
static { static {
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> Settings.fromXContent(p), SETTINGS); PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> Settings.fromXContent(p), SETTINGS);
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> PARSER.declareField(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> {
new CompressedXContent(Strings.toString(XContentFactory.jsonBuilder().map(p.mapOrdered()))), MAPPINGS); XContentParser.Token token = p.currentToken();
if (token == XContentParser.Token.VALUE_STRING) {
return new CompressedXContent(Base64.getDecoder().decode(p.text()));
} else if(token == XContentParser.Token.VALUE_EMBEDDED_OBJECT){
return new CompressedXContent(p.binaryValue());
} else if (token == XContentParser.Token.START_OBJECT){
return new CompressedXContent(Strings.toString(XContentFactory.jsonBuilder().map(p.mapOrdered())));
} else {
throw new IllegalArgumentException("Unexpected token: " + token);
}
}, MAPPINGS, ObjectParser.ValueType.VALUE_OBJECT_ARRAY);
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> { PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> {
Map<String, AliasMetadata> aliasMap = new HashMap<>(); Map<String, AliasMetadata> aliasMap = new HashMap<>();
while ((p.nextToken()) != XContentParser.Token.END_OBJECT) { while ((p.nextToken()) != XContentParser.Token.END_OBJECT) {
@ -160,11 +172,17 @@ public class Template extends AbstractDiffable<Template> implements ToXContentOb
builder.endObject(); builder.endObject();
} }
if (this.mappings != null) { if (this.mappings != null) {
Map<String, Object> uncompressedMapping = String context = params.param(Metadata.CONTEXT_MODE_PARAM, Metadata.CONTEXT_MODE_API);
XContentHelper.convertToMap(this.mappings.uncompressed(), true, XContentType.JSON).v2(); boolean binary = params.paramAsBoolean("binary", false);
if (uncompressedMapping.size() > 0) { if (Metadata.CONTEXT_MODE_API.equals(context) || binary == false) {
builder.field(MAPPINGS.getPreferredName()); Map<String, Object> uncompressedMapping =
builder.map(reduceMapping(uncompressedMapping)); XContentHelper.convertToMap(this.mappings.uncompressed(), true, XContentType.JSON).v2();
if (uncompressedMapping.size() > 0) {
builder.field(MAPPINGS.getPreferredName());
builder.map(reduceMapping(uncompressedMapping));
}
} else {
builder.field(MAPPINGS.getPreferredName(), mappings.compressed());
} }
} }
if (this.aliases != null) { if (this.aliases != null) {