Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
5 changes: 5 additions & 0 deletions docs/changelog/120573.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 120573
summary: Optimize `IngestDocument` `FieldPath` allocation
area: Ingest Node
type: enhancement
issues: []
48 changes: 34 additions & 14 deletions server/src/main/java/org/elasticsearch/ingest/IngestDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.common.util.Maps;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.core.UpdateForV10;
import org.elasticsearch.index.VersionType;
Expand Down Expand Up @@ -187,8 +188,8 @@ public <T> T getFieldValue(String path, Class<T> clazz) {
* or if the field that is found at the provided path is not of the expected type.
*/
public <T> T getFieldValue(String path, Class<T> clazz, boolean ignoreMissing) {
FieldPath fieldPath = new FieldPath(path);
Object context = fieldPath.initialContext;
final FieldPath fieldPath = FieldPath.of(path);
Object context = fieldPath.initialContext(this);
for (String pathElement : fieldPath.pathElements) {
ResolveResult result = resolve(pathElement, path, context);
if (result.wasSuccessful) {
Expand Down Expand Up @@ -258,8 +259,8 @@ public boolean hasField(String path) {
* @throws IllegalArgumentException if the path is null, empty or invalid.
*/
public boolean hasField(String path, boolean failOutOfRange) {
FieldPath fieldPath = new FieldPath(path);
Object context = fieldPath.initialContext;
final FieldPath fieldPath = FieldPath.of(path);
Object context = fieldPath.initialContext(this);
for (int i = 0; i < fieldPath.pathElements.length - 1; i++) {
String pathElement = fieldPath.pathElements[i];
if (context == null) {
Expand Down Expand Up @@ -326,8 +327,8 @@ public boolean hasField(String path, boolean failOutOfRange) {
* @throws IllegalArgumentException if the path is null, empty, invalid or if the field doesn't exist.
*/
public void removeField(String path) {
FieldPath fieldPath = new FieldPath(path);
Object context = fieldPath.initialContext;
final FieldPath fieldPath = FieldPath.of(path);
Object context = fieldPath.initialContext(this);
for (int i = 0; i < fieldPath.pathElements.length - 1; i++) {
ResolveResult result = resolve(fieldPath.pathElements[i], path, context);
if (result.wasSuccessful) {
Expand Down Expand Up @@ -537,8 +538,8 @@ public void setFieldValue(String path, Object value, boolean ignoreEmptyValue) {
}

private void setFieldValue(String path, Object value, boolean append, boolean allowDuplicates) {
FieldPath fieldPath = new FieldPath(path);
Object context = fieldPath.initialContext;
final FieldPath fieldPath = FieldPath.of(path);
Object context = fieldPath.initialContext(this);
for (int i = 0; i < fieldPath.pathElements.length - 1; i++) {
String pathElement = fieldPath.pathElements[i];
if (context == null) {
Expand Down Expand Up @@ -990,21 +991,37 @@ public String getFieldName() {
}
}

private class FieldPath {
private static final class FieldPath {

private final String[] pathElements;
private final Object initialContext;
private static final int MAX_SIZE = 512;
private static final Map<String, FieldPath> CACHE = ConcurrentCollections.newConcurrentMapWithAggressiveConcurrency();

private FieldPath(String path) {
static FieldPath of(String path) {
if (Strings.isEmpty(path)) {
throw new IllegalArgumentException("path cannot be null nor empty");
}
FieldPath res = CACHE.get(path);
if (res != null) {
return res;
}
res = new FieldPath(path);
if (CACHE.size() > MAX_SIZE) {
CACHE.clear();
}
CACHE.put(path, res);
return res;
}

private final String[] pathElements;
private final boolean useIngestContext;

private FieldPath(String path) {
String newPath;
if (path.startsWith(INGEST_KEY_PREFIX)) {
initialContext = ingestMetadata;
useIngestContext = true;
newPath = path.substring(INGEST_KEY_PREFIX.length());
} else {
initialContext = ctxMap;
useIngestContext = false;
if (path.startsWith(SOURCE_PREFIX)) {
newPath = path.substring(SOURCE_PREFIX.length());
} else {
Expand All @@ -1017,6 +1034,9 @@ private FieldPath(String path) {
}
}

public Object initialContext(IngestDocument document) {
return useIngestContext ? document.getIngestMetadata() : document.getCtxMap();
}
}

private static class ResolveResult {
Expand Down