Skip to content

Commit

Permalink
Ignore conflicting fields during dynamic mapping update
Browse files Browse the repository at this point in the history
This fixes a bug when concurrently executing index requests that have different types for the same field.
  • Loading branch information
felixbarny committed Oct 7, 2024
1 parent 0c69de1 commit ae14bc7
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.oneOf;

public class DynamicMappingIT extends ESIntegTestCase {

Expand Down Expand Up @@ -190,6 +192,35 @@ private Map<String, Object> indexConcurrently(int numberOfFieldsToCreate, Settin
return properties;
}

public void testConcurrentDynamicMappingsWithConflictingType() throws Throwable {
int numberOfDocsToCreate = 16;
indicesAdmin().prepareCreate("index").setSettings(Settings.builder()).get();
ensureGreen("index");
final AtomicReference<Throwable> error = new AtomicReference<>();
startInParallel(numberOfDocsToCreate, i -> {
try {
assertEquals(
DocWriteResponse.Result.CREATED,
prepareIndex("index").setId(Integer.toString(i)).setSource("field" + i, 0, "field" + (i + 1), 0.1).get().getResult()
);
} catch (Exception e) {
error.compareAndSet(null, e);
}
});
if (error.get() != null) {
throw error.get();
}
client().admin().indices().prepareRefresh("index").get();
for (int i = 0; i < numberOfDocsToCreate; ++i) {
assertTrue(client().prepareGet("index", Integer.toString(i)).get().isExists());
}
Map<String, Object> index = indicesAdmin().prepareGetMappings("index").get().getMappings().get("index").getSourceAsMap();
for (int i = 0, j = 1; i < numberOfDocsToCreate; i++, j++) {
assertThat(new WriteField("properties.field" + i + ".type", () -> index).get(null), is(oneOf("long", "float")));
assertThat(new WriteField("properties.field" + j + ".type", () -> index).get(null), is(oneOf("long", "float")));
}
}

public void testPreflightCheckAvoidsMaster() throws InterruptedException, IOException {
// can't use INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING nor INDEX_MAPPING_DEPTH_LIMIT_SETTING as a check here, as that is already
// checked at parse time, see testTotalFieldsLimitForDynamicMappingsUpdateCheckedAtDocumentParseTime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -677,14 +677,30 @@ private static Map<String, Mapper> buildMergedMappers(
// replaces an existing one.
if (objectMergeContext.getMapperBuilderContext().getMergeReason() == MergeReason.INDEX_TEMPLATE) {
putMergedMapper(mergedMappers, mergeWithMapper);
} else {
} else if (isConflictingDynamicMapping(objectMergeContext, mergeWithMapper, mergeIntoMapper) == false) {
putMergedMapper(mergedMappers, mergeIntoMapper.merge(mergeWithMapper, objectMergeContext));
}
}
}
return Map.copyOf(mergedMappers);
}

/*
* We're ignoring the field if a dynamic mapping update tries to define a conflicting field type (dynamic mappings update)
* This is caused by another index request with a different value racing to update the mappings.
* After ignoring the update the index request will be re-tried and sees the updated mappings for this field.
* The updated mappings will be taken into account when parsing the document
* (for example by coercing the value, ignore_malformed values, or failing the index request due to a type conflict).
*/
private static boolean isConflictingDynamicMapping(
MapperMergeContext objectMergeContext,
Mapper mergeWithMapper,
Mapper mergeIntoMapper
) {
return objectMergeContext.getMapperBuilderContext().getMergeReason().isAutoUpdate()
&& mergeIntoMapper.typeName().equals(mergeWithMapper.typeName()) == false;
}

private static void putMergedMapper(Map<String, Mapper> mergedMappers, @Nullable Mapper merged) {
if (merged != null) {
mergedMappers.put(merged.leafName(), merged);
Expand Down

0 comments on commit ae14bc7

Please sign in to comment.