Skip to content

Save heap when dynamically mapping arrays #127439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/changelog/127439.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pr: 127439
summary: Save heap when dynamically mapping arrays
area: Mapping
type: bug
issues:
- 117593
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ public void testArrayWithDifferentTypes() {
.get();

assertTrue(bulkResponse.hasFailures());
assertEquals(
"mapper [foo] cannot be changed from type [long] to [text]",
bulkResponse.getItems()[0].getFailure().getCause().getMessage()
assertThat(
bulkResponse.getItems()[0].getFailure().getCause().getMessage(),
containsString("mapper [foo] cannot be changed from type [long] to [text]")
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

import java.io.IOException;
import java.time.DateTimeException;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
Expand Down Expand Up @@ -312,6 +314,11 @@ private static final class Concrete implements Strategy {
boolean createDynamicField(Mapper.Builder builder, DocumentParserContext context, MapperBuilderContext mapperBuilderContext)
throws IOException {
Mapper mapper = builder.build(mapperBuilderContext);
List<Mapper> existing;
if ((existing = context.getDynamicMappers(mapper.fullPath())) != null && existing.isEmpty() == false) {
maybeMergeWithExistingField(context, mapperBuilderContext, mapper, existing);
return true;
}
if (context.addDynamicMapper(mapper)) {
parseField.accept(context, mapper);
return true;
Expand All @@ -320,6 +327,26 @@ boolean createDynamicField(Mapper.Builder builder, DocumentParserContext context
}
}

// Attempts to deduplicate arrays of mapper instances my merging with the most recently added mapper for the same path
// if there is one already and then replacing all entries for the field with the merge result
// TODO: this is only as complicated as it is to enable DocumentParser.postProcessDynamicArrayMapping, technically there is no need
// to duplicate the mapper other than to enabled this method's translation of multiple numeric field mappers into a vector mapper
// it seems?
private void maybeMergeWithExistingField(
DocumentParserContext context,
MapperBuilderContext mapperBuilderContext,
Mapper fieldMapper,
List<Mapper> existing
) throws IOException {
var last = existing.getLast();
var merged = last.merge(fieldMapper, MapperMergeContext.from(mapperBuilderContext, Long.MAX_VALUE));
if (merged != last) {
Collections.fill(existing, merged);
}
existing.add(merged);
parseField.accept(context, merged);
}

boolean createDynamicField(Mapper.Builder builder, DocumentParserContext context) throws IOException {
return createDynamicField(builder, context, context.createDynamicMapperBuilderContext());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ public class ObjectMapperTests extends MapperServiceTestCase {

public void testDifferentInnerObjectTokenFailure() throws Exception {
DocumentMapper defaultMapper = createDocumentMapper(mapping(b -> {}));
IllegalArgumentException e = expectThrows(
IllegalArgumentException.class,
DocumentParsingException e = expectThrows(
DocumentParsingException.class,
() -> defaultMapper.parse(new SourceToParse("1", new BytesArray("""
{
"object": {
Expand Down
Loading