23
23
import org .elasticsearch .ingest .ConfigurationUtils ;
24
24
import org .elasticsearch .ingest .IngestDocument ;
25
25
import org .elasticsearch .ingest .Processor ;
26
+ import org .elasticsearch .ingest .WrappingProcessor ;
27
+ import org .elasticsearch .script .ScriptService ;
26
28
27
29
import java .util .ArrayList ;
28
30
import java .util .List ;
29
31
import java .util .Map ;
30
32
import java .util .Set ;
31
33
import java .util .concurrent .CopyOnWriteArrayList ;
32
34
import java .util .function .BiConsumer ;
33
-
34
- import org .elasticsearch .ingest .WrappingProcessor ;
35
- import org .elasticsearch .script .ScriptService ;
35
+ import java .util .function .Consumer ;
36
36
37
37
import static org .elasticsearch .ingest .ConfigurationUtils .newConfigurationException ;
38
38
import static org .elasticsearch .ingest .ConfigurationUtils .readBooleanProperty ;
50
50
public final class ForEachProcessor extends AbstractProcessor implements WrappingProcessor {
51
51
52
52
public static final String TYPE = "foreach" ;
53
+ static final int MAX_RECURSE_PER_THREAD = 10 ;
53
54
54
55
private final String field ;
55
56
private final Processor processor ;
56
57
private final boolean ignoreMissing ;
58
+ private final Consumer <Runnable > genericExecutor ;
57
59
58
- ForEachProcessor (String tag , String field , Processor processor , boolean ignoreMissing ) {
60
+ ForEachProcessor (String tag , String field , Processor processor , boolean ignoreMissing , Consumer < Runnable > genericExecutor ) {
59
61
super (tag );
60
62
this .field = field ;
61
63
this .processor = processor ;
62
64
this .ignoreMissing = ignoreMissing ;
65
+ this .genericExecutor = genericExecutor ;
63
66
}
64
67
65
68
boolean isIgnoreMissing () {
@@ -91,6 +94,7 @@ void innerExecute(int index, List<?> values, List<Object> newValues, IngestDocum
91
94
92
95
Object value = values .get (index );
93
96
Object previousValue = document .getIngestMetadata ().put ("_value" , value );
97
+ final Thread thread = Thread .currentThread ();
94
98
processor .execute (document , (result , e ) -> {
95
99
if (e != null ) {
96
100
newValues .add (document .getIngestMetadata ().put ("_value" , previousValue ));
@@ -99,7 +103,15 @@ void innerExecute(int index, List<?> values, List<Object> newValues, IngestDocum
99
103
handler .accept (null , null );
100
104
} else {
101
105
newValues .add (document .getIngestMetadata ().put ("_value" , previousValue ));
102
- innerExecute (index + 1 , values , newValues , document , handler );
106
+ if (thread == Thread .currentThread () && (index + 1 ) % MAX_RECURSE_PER_THREAD == 0 ) {
107
+ // we are on the same thread and we need to fork to another thread to avoid recursive stack overflow on a single thread
108
+ // only fork after 10 recursive calls, then fork every 10 to keep the number of threads down
109
+ genericExecutor .accept (() -> innerExecute (index + 1 , values , newValues , document , handler ));
110
+ } else {
111
+ // we are on a different thread (we went asynchronous), it's safe to recurse
112
+ // or we have recursed less then 10 times with the same thread, it's safe to recurse
113
+ innerExecute (index + 1 , values , newValues , document , handler );
114
+ }
103
115
}
104
116
});
105
117
}
@@ -125,9 +137,11 @@ public Processor getInnerProcessor() {
125
137
public static final class Factory implements Processor .Factory {
126
138
127
139
private final ScriptService scriptService ;
140
+ private final Consumer <Runnable > genericExecutor ;
128
141
129
- Factory (ScriptService scriptService ) {
142
+ Factory (ScriptService scriptService , Consumer < Runnable > genericExecutor ) {
130
143
this .scriptService = scriptService ;
144
+ this .genericExecutor = genericExecutor ;
131
145
}
132
146
133
147
@ Override
@@ -143,7 +157,7 @@ public ForEachProcessor create(Map<String, Processor.Factory> factories, String
143
157
Map .Entry <String , Map <String , Object >> entry = entries .iterator ().next ();
144
158
Processor processor =
145
159
ConfigurationUtils .readProcessor (factories , scriptService , entry .getKey (), entry .getValue ());
146
- return new ForEachProcessor (tag , field , processor , ignoreMissing );
160
+ return new ForEachProcessor (tag , field , processor , ignoreMissing , genericExecutor );
147
161
}
148
162
}
149
163
}
0 commit comments