This is an automated email from the ASF dual-hosted git repository.
asalamon74 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/oozie.git
The following commit(s) were added to refs/heads/master by this push:
new 06cf2cf OOZIE-3561 Forkjoin validation is slow when there are many
actions in chain (dionusos, pbacsko via asalamon74)
06cf2cf is described below
commit 06cf2cf005b3f98bcd40a1111934a02b530fac07
Author: Andras Salamon <[email protected]>
AuthorDate: Wed Nov 27 12:29:18 2019 +0100
OOZIE-3561 Forkjoin validation is slow when there are many actions in chain
(dionusos, pbacsko via asalamon74)
---
.../oozie/workflow/lite/LiteWorkflowValidator.java | 63 ++++++++++---
.../workflow/lite/TestLiteWorkflowAppParser.java | 69 ++++++++++++++
core/src/test/resources/wf-actions-20.xml | 43 +++++++++
core/src/test/resources/wf-actions-40.xml | 63 +++++++++++++
core/src/test/resources/wf-actions-80.xml | 102 +++++++++++++++++++++
release-log.txt | 1 +
6 files changed, 330 insertions(+), 11 deletions(-)
diff --git
a/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowValidator.java
b/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowValidator.java
index eceb019..e6102d8 100644
---
a/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowValidator.java
+++
b/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowValidator.java
@@ -34,12 +34,14 @@ import org.apache.oozie.ErrorCode;
import org.apache.oozie.service.ActionService;
import org.apache.oozie.service.Services;
import org.apache.oozie.util.ParamChecker;
+import org.apache.oozie.util.XLog;
import org.apache.oozie.util.XmlUtils;
import org.apache.oozie.workflow.WorkflowException;
import org.jdom.Element;
import org.jdom.JDOMException;
public class LiteWorkflowValidator {
+ private static XLog LOG = XLog.getLog(LiteWorkflowValidator.class);
public void validateWorkflow(LiteWorkflowApp app, boolean
validateForkJoin) throws WorkflowException {
NodeDef startNode = app.getNode(StartNodeDef.START);
@@ -64,7 +66,8 @@ public class LiteWorkflowValidator {
true,
new ArrayDeque<String>(),
new HashMap<String, String>(),
- new HashMap<String, Optional<String>>());
+ new HashMap<String, Optional<String>>(),
+ new HashSet<>());
}
}
@@ -135,11 +138,18 @@ public class LiteWorkflowValidator {
* already been visited at least once before
* @param forkJoins Map that contains a mapping of fork-join node pairs.
* @param nodeAndDecisionParents Map that contains a mapping of nodes and
their eldest decision node
+ * @param visitedNodes contains the nodes that have been already visited &
validated (except Join/End nodes)
* @throws WorkflowException If there is any of the constraints described
above is violated
*/
- private void validateForkJoin(LiteWorkflowApp app, NodeDef node, NodeDef
currentFork, String topDecisionParent,
- boolean okPath, Deque<String> path, Map<String, String> forkJoins,
- Map<String, Optional<String>> nodeAndDecisionParents) throws
WorkflowException {
+ private void validateForkJoin(LiteWorkflowApp app,
+ NodeDef node,
+ NodeDef currentFork,
+ String topDecisionParent,
+ boolean okPath,
+ Deque<String> path,
+ Map<String, String> forkJoins,
+ Map<String, Optional<String>> nodeAndDecisionParents,
+ Set<NodeDef> visitedNodes) throws WorkflowException {
final String nodeName = node.getName();
path.addLast(nodeName);
@@ -186,6 +196,33 @@ public class LiteWorkflowValidator {
}
}
+ /* Memoization part: don't re-walk paths that have been visited
already. This prevents
+ * exponential runtime in specific cases.
+ *
+ * There are three edge-cases that we have to keep in mind:
+ * 1. This part of the code cannot be above the "okTo" verification
part. Otherwise we would
+ * accept WFs where multiple "ok" paths lead to the same node.
+ *
+ * 2. We don't store Join nodes. Firstly, we don't recurse from Join
nodes anyway.
+ * Also, it's necessary to reach fork-join mapping verification below,
+ * so that we can throw errors "E0742" or "E0758" if needed.
+ *
+ * 3. We don't store End nodes. Similarly to Join, no recursion occurs
after End. Plus, we
+ * could miss the erroneous condition "E0737" if we previously arrived
at End from a valid path.
+ */
+ if (visitedNodes.contains(node)) {
+ LOG.debug("Skipping node because it's been validated: " +
nodeName);
+ path.remove(nodeName);
+ return;
+ } else {
+ if (node instanceof JoinNodeDef || node instanceof EndNodeDef) {
+ LOG.debug("Not storing node because it's a Join or End: " +
nodeName);
+ } else {
+ visitedNodes.add(node);
+ LOG.debug("Storing node as visited: " + nodeName);
+ }
+ }
+
/* Fork-Join validation logic:
*
* At each Fork node, we recurse to every possible paths, changing the
"currentFork" variable to the Fork node. We stop
@@ -211,7 +248,8 @@ public class LiteWorkflowValidator {
for (String t : transitions) {
NodeDef transition = app.getNode(t);
- validateForkJoin(app, transition, node, topDecisionParent,
okPath, path, forkJoins, nodeAndDecisionParents);
+ validateForkJoin(app, transition, node, topDecisionParent,
okPath, path, forkJoins, nodeAndDecisionParents,
+ visitedNodes);
}
// get the Join node for this ForkNode & validate it (we must have
only one)
@@ -222,7 +260,8 @@ public class LiteWorkflowValidator {
List<String> joinTransitions =
app.getNode(joins.iterator().next()).getTransitions();
NodeDef next = app.getNode(joinTransitions.get(0));
- validateForkJoin(app, next, currentFork, topDecisionParent,
okPath, path, forkJoins, nodeAndDecisionParents);
+ validateForkJoin(app, next, currentFork, topDecisionParent,
okPath, path, forkJoins, nodeAndDecisionParents,
+ visitedNodes);
} else if (node instanceof JoinNodeDef) {
if (currentFork == null) {
throw new WorkflowException(ErrorCode.E0742, node.getName());
@@ -247,7 +286,7 @@ public class LiteWorkflowValidator {
for (String t : transitions) {
NodeDef transition = app.getNode(t);
validateForkJoin(app, transition, currentFork,
parentDecisionNode, okPath, path, forkJoins,
- nodeAndDecisionParents);
+ nodeAndDecisionParents, visitedNodes);
}
} else if (node instanceof KillNodeDef) {
// no op
@@ -262,19 +301,21 @@ public class LiteWorkflowValidator {
} else if (node instanceof ActionNodeDef) {
String transition = node.getTransitions().get(0); // "ok to"
transition
NodeDef okNode = app.getNode(transition);
- validateForkJoin(app, okNode, currentFork, topDecisionParent,
okPath, path, forkJoins, nodeAndDecisionParents);
+ validateForkJoin(app, okNode, currentFork, topDecisionParent,
okPath, path, forkJoins, nodeAndDecisionParents,
+ visitedNodes);
transition = node.getTransitions().get(1); // "error to"
transition
NodeDef errorNode = app.getNode(transition);
- validateForkJoin(app, errorNode, currentFork, topDecisionParent,
false, path, forkJoins, nodeAndDecisionParents);
+ validateForkJoin(app, errorNode, currentFork, topDecisionParent,
false, path, forkJoins, nodeAndDecisionParents,
+ visitedNodes);
} else if (node instanceof StartNodeDef) {
String transition = node.getTransitions().get(0); // start
always has only 1 transition
NodeDef tranNode = app.getNode(transition);
- validateForkJoin(app, tranNode, currentFork, topDecisionParent,
okPath, path, forkJoins, nodeAndDecisionParents);
+ validateForkJoin(app, tranNode, currentFork, topDecisionParent,
okPath, path, forkJoins, nodeAndDecisionParents,
+ visitedNodes);
} else {
throw new WorkflowException(ErrorCode.E0740, node.getClass());
}
-
path.remove(nodeName);
}
diff --git
a/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java
b/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java
index 1389d3e..157d406 100644
---
a/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java
+++
b/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java
@@ -1292,6 +1292,75 @@ public class TestLiteWorkflowAppParser extends XTestCase
{
}
}
+ public void testMultiplePathsToEnd() throws Exception {
+ // Makes sure that despite using memoization, the validator
+ // still finds incorrect state transition to "end" nodes
+ LiteWorkflowAppParser parser = newLiteWorkflowAppParser();
+
+ LiteWorkflowApp def = new LiteWorkflowApp("name", "def",
+ new
StartNodeDef(LiteWorkflowStoreService.LiteControlNodeHandler.class, "one"))
+ .addNode(new ActionNodeDef("one", dummyConf,
TestActionNodeHandler.class, "end", "f"))
+ .addNode(new ForkNodeDef("f",
LiteWorkflowStoreService.LiteControlNodeHandler.class,
+ Arrays.asList(new String[]{"two", "three"})))
+ .addNode(new ActionNodeDef("two", dummyConf,
TestActionNodeHandler.class, "end", "k")) // invalid
+ .addNode(new ActionNodeDef("three", dummyConf,
TestActionNodeHandler.class, "j", "k"))
+ .addNode(new JoinNodeDef("j",
LiteWorkflowStoreService.LiteControlNodeHandler.class, "end"))
+ .addNode(new KillNodeDef("k", "kill",
LiteWorkflowStoreService.LiteControlNodeHandler.class))
+ .addNode(new EndNodeDef("end",
LiteWorkflowStoreService.LiteControlNodeHandler.class));
+
+ try {
+ invokeForkJoin(parser, def);
+ fail("Expected to catch an exception but did not encounter any");
+ } catch (WorkflowException we) {
+ assertEquals(ErrorCode.E0737, we.getErrorCode());
+ assertTrue(we.getMessage().contains("[two]"));
+ }
+ }
+
+ public void testRuntimeWith20Actions() throws Exception {
+ testRuntimeWithActions("wf-actions-20.xml");
+ }
+
+ public void testRuntimeWith40Actions() throws Exception {
+ testRuntimeWithActions("wf-actions-40.xml");
+ }
+
+ public void testRuntimeWith80Actions() throws Exception {
+ testRuntimeWithActions("wf-actions-80.xml");
+ }
+
+ @SuppressWarnings("deprecation")
+ private void testRuntimeWithActions(String workflowXml) throws Exception {
+ LiteWorkflowAppParser parser = newLiteWorkflowAppParser();
+
+ final AtomicBoolean failure = new AtomicBoolean(false);
+ final AtomicBoolean finished = new AtomicBoolean(false);
+
+ Runnable r = () -> {
+ try {
+ parser.validateAndParse(IOUtils.getResourceAsReader(
+ workflowXml, -1), new Configuration());
+ } catch (final Exception e) {
+ e.printStackTrace();
+ failure.set(true);
+ }
+
+ finished.set(true);
+ };
+
+ Thread t = new Thread(r);
+ t.setName("Workflow validator thread for " + workflowXml);
+ t.start();
+ t.join((long) (2000 * XTestCase.WAITFOR_RATIO));
+
+ if (!finished.get()) {
+ t.stop(); // don't let the validation keep running in the
background which causes high CPU load
+ fail("Workflow validation did not finish in time");
+ }
+
+ assertFalse("Workflow validation failed", failure.get());
+ }
+
private void invokeForkJoin(LiteWorkflowAppParser parser, LiteWorkflowApp
def) throws WorkflowException {
LiteWorkflowValidator validator = new LiteWorkflowValidator();
validator.validateWorkflow(def, true);
diff --git a/core/src/test/resources/wf-actions-20.xml
b/core/src/test/resources/wf-actions-20.xml
new file mode 100644
index 0000000..645fdb3
--- /dev/null
+++ b/core/src/test/resources/wf-actions-20.xml
@@ -0,0 +1,43 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<workflow-app xmlns="uri:oozie:workflow:0.5" name="test-wf">
+ <start to="a1"/>
+
+ <action name="a1"><fs><mkdir path='/tmp'/></fs><ok to="a2"/><error
to="a2"/></action>
+ <action name="a2"><fs><mkdir path='/tmp'/></fs><ok to="a3"/><error
to="a3"/></action>
+ <action name="a3"><fs><mkdir path='/tmp'/></fs><ok to="a4"/><error
to="a4"/></action>
+ <action name="a4"><fs><mkdir path='/tmp'/></fs><ok to="a5"/><error
to="a5"/></action>
+ <action name="a5"><fs><mkdir path='/tmp'/></fs><ok to="a6"/><error
to="a6"/></action>
+ <action name="a6"><fs><mkdir path='/tmp'/></fs><ok to="a7"/><error
to="a7"/></action>
+ <action name="a7"><fs><mkdir path='/tmp'/></fs><ok to="a8"/><error
to="a8"/></action>
+ <action name="a8"><fs><mkdir path='/tmp'/></fs><ok to="a9"/><error
to="a9"/></action>
+ <action name="a9"><fs><mkdir path='/tmp'/></fs><ok to="a10"/><error
to="a10"/></action>
+ <action name="a10"><fs><mkdir path='/tmp'/></fs><ok to="a11"/><error
to="a11"/></action>
+ <action name="a11"><fs><mkdir path='/tmp'/></fs><ok to="a12"/><error
to="a12"/></action>
+ <action name="a12"><fs><mkdir path='/tmp'/></fs><ok to="a13"/><error
to="a13"/></action>
+ <action name="a13"><fs><mkdir path='/tmp'/></fs><ok to="a14"/><error
to="a14"/></action>
+ <action name="a14"><fs><mkdir path='/tmp'/></fs><ok to="a15"/><error
to="a15"/></action>
+ <action name="a15"><fs><mkdir path='/tmp'/></fs><ok to="a16"/><error
to="a16"/></action>
+ <action name="a16"><fs><mkdir path='/tmp'/></fs><ok to="a17"/><error
to="a17"/></action>
+ <action name="a17"><fs><mkdir path='/tmp'/></fs><ok to="a18"/><error
to="a18"/></action>
+ <action name="a18"><fs><mkdir path='/tmp'/></fs><ok to="a19"/><error
to="a19"/></action>
+ <action name="a19"><fs><mkdir path='/tmp'/></fs><ok to="a20"/><error
to="a20"/></action>
+ <action name="a20"><fs><mkdir path='/tmp'/></fs><ok to="z"/><error
to="z"/></action>
+
+ <end name="z"/>
+</workflow-app>
\ No newline at end of file
diff --git a/core/src/test/resources/wf-actions-40.xml
b/core/src/test/resources/wf-actions-40.xml
new file mode 100644
index 0000000..256c19c
--- /dev/null
+++ b/core/src/test/resources/wf-actions-40.xml
@@ -0,0 +1,63 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<workflow-app xmlns="uri:oozie:workflow:0.5" name="test-wf">
+ <start to="a1"/>
+
+ <action name="a1"><fs><mkdir path='/tmp'/></fs><ok to="a2"/><error
to="a2"/></action>
+ <action name="a2"><fs><mkdir path='/tmp'/></fs><ok to="a3"/><error
to="a3"/></action>
+ <action name="a3"><fs><mkdir path='/tmp'/></fs><ok to="a4"/><error
to="a4"/></action>
+ <action name="a4"><fs><mkdir path='/tmp'/></fs><ok to="a5"/><error
to="a5"/></action>
+ <action name="a5"><fs><mkdir path='/tmp'/></fs><ok to="a6"/><error
to="a6"/></action>
+ <action name="a6"><fs><mkdir path='/tmp'/></fs><ok to="a7"/><error
to="a7"/></action>
+ <action name="a7"><fs><mkdir path='/tmp'/></fs><ok to="a8"/><error
to="a8"/></action>
+ <action name="a8"><fs><mkdir path='/tmp'/></fs><ok to="a9"/><error
to="a9"/></action>
+ <action name="a9"><fs><mkdir path='/tmp'/></fs><ok to="a10"/><error
to="a10"/></action>
+ <action name="a10"><fs><mkdir path='/tmp'/></fs><ok to="a11"/><error
to="a11"/></action>
+ <action name="a11"><fs><mkdir path='/tmp'/></fs><ok to="a12"/><error
to="a12"/></action>
+ <action name="a12"><fs><mkdir path='/tmp'/></fs><ok to="a13"/><error
to="a13"/></action>
+ <action name="a13"><fs><mkdir path='/tmp'/></fs><ok to="a14"/><error
to="a14"/></action>
+ <action name="a14"><fs><mkdir path='/tmp'/></fs><ok to="a15"/><error
to="a15"/></action>
+ <action name="a15"><fs><mkdir path='/tmp'/></fs><ok to="a16"/><error
to="a16"/></action>
+ <action name="a16"><fs><mkdir path='/tmp'/></fs><ok to="a17"/><error
to="a17"/></action>
+ <action name="a17"><fs><mkdir path='/tmp'/></fs><ok to="a18"/><error
to="a18"/></action>
+ <action name="a18"><fs><mkdir path='/tmp'/></fs><ok to="a19"/><error
to="a19"/></action>
+ <action name="a19"><fs><mkdir path='/tmp'/></fs><ok to="a20"/><error
to="a20"/></action>
+ <action name="a20"><fs><mkdir path='/tmp'/></fs><ok to="a21"/><error
to="a21"/></action>
+ <action name="a21"><fs><mkdir path='/tmp'/></fs><ok to="a22"/><error
to="a22"/></action>
+ <action name="a22"><fs><mkdir path='/tmp'/></fs><ok to="a23"/><error
to="a23"/></action>
+ <action name="a23"><fs><mkdir path='/tmp'/></fs><ok to="a24"/><error
to="a24"/></action>
+ <action name="a24"><fs><mkdir path='/tmp'/></fs><ok to="a25"/><error
to="a25"/></action>
+ <action name="a25"><fs><mkdir path='/tmp'/></fs><ok to="a26"/><error
to="a26"/></action>
+ <action name="a26"><fs><mkdir path='/tmp'/></fs><ok to="a27"/><error
to="a27"/></action>
+ <action name="a27"><fs><mkdir path='/tmp'/></fs><ok to="a28"/><error
to="a28"/></action>
+ <action name="a28"><fs><mkdir path='/tmp'/></fs><ok to="a29"/><error
to="a29"/></action>
+ <action name="a29"><fs><mkdir path='/tmp'/></fs><ok to="a30"/><error
to="a30"/></action>
+ <action name="a30"><fs><mkdir path='/tmp'/></fs><ok to="a31"/><error
to="a31"/></action>
+ <action name="a31"><fs><mkdir path='/tmp'/></fs><ok to="a32"/><error
to="a32"/></action>
+ <action name="a32"><fs><mkdir path='/tmp'/></fs><ok to="a33"/><error
to="a33"/></action>
+ <action name="a33"><fs><mkdir path='/tmp'/></fs><ok to="a34"/><error
to="a34"/></action>
+ <action name="a34"><fs><mkdir path='/tmp'/></fs><ok to="a35"/><error
to="a35"/></action>
+ <action name="a35"><fs><mkdir path='/tmp'/></fs><ok to="a36"/><error
to="a36"/></action>
+ <action name="a36"><fs><mkdir path='/tmp'/></fs><ok to="a37"/><error
to="a37"/></action>
+ <action name="a37"><fs><mkdir path='/tmp'/></fs><ok to="a38"/><error
to="a38"/></action>
+ <action name="a38"><fs><mkdir path='/tmp'/></fs><ok to="a39"/><error
to="a39"/></action>
+ <action name="a39"><fs><mkdir path='/tmp'/></fs><ok to="a40"/><error
to="a40"/></action>
+ <action name="a40"><fs><mkdir path='/tmp'/></fs><ok to="z"/><error
to="z"/></action>
+
+ <end name="z"/>
+</workflow-app>
\ No newline at end of file
diff --git a/core/src/test/resources/wf-actions-80.xml
b/core/src/test/resources/wf-actions-80.xml
new file mode 100644
index 0000000..95a623b
--- /dev/null
+++ b/core/src/test/resources/wf-actions-80.xml
@@ -0,0 +1,102 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<workflow-app xmlns="uri:oozie:workflow:0.5" name="test-wf">
+ <start to="a1"/>
+
+ <action name="a1"><fs><mkdir path='/tmp'/></fs><ok to="a2"/><error
to="a2"/></action>
+ <action name="a2"><fs><mkdir path='/tmp'/></fs><ok to="a3"/><error
to="a3"/></action>
+ <action name="a3"><fs><mkdir path='/tmp'/></fs><ok to="a4"/><error
to="a4"/></action>
+ <action name="a4"><fs><mkdir path='/tmp'/></fs><ok to="a5"/><error
to="a5"/></action>
+ <action name="a5"><fs><mkdir path='/tmp'/></fs><ok to="a6"/><error
to="a6"/></action>
+ <action name="a6"><fs><mkdir path='/tmp'/></fs><ok to="a7"/><error
to="a7"/></action>
+ <action name="a7"><fs><mkdir path='/tmp'/></fs><ok to="a8"/><error
to="a8"/></action>
+ <action name="a8"><fs><mkdir path='/tmp'/></fs><ok to="a9"/><error
to="a9"/></action>
+ <action name="a9"><fs><mkdir path='/tmp'/></fs><ok to="a10"/><error
to="a10"/></action>
+ <action name="a10"><fs><mkdir path='/tmp'/></fs><ok to="a11"/><error
to="a11"/></action>
+ <action name="a11"><fs><mkdir path='/tmp'/></fs><ok to="a12"/><error
to="a12"/></action>
+ <action name="a12"><fs><mkdir path='/tmp'/></fs><ok to="a13"/><error
to="a13"/></action>
+ <action name="a13"><fs><mkdir path='/tmp'/></fs><ok to="a14"/><error
to="a14"/></action>
+ <action name="a14"><fs><mkdir path='/tmp'/></fs><ok to="a15"/><error
to="a15"/></action>
+ <action name="a15"><fs><mkdir path='/tmp'/></fs><ok to="a16"/><error
to="a16"/></action>
+ <action name="a16"><fs><mkdir path='/tmp'/></fs><ok to="a17"/><error
to="a17"/></action>
+ <action name="a17"><fs><mkdir path='/tmp'/></fs><ok to="a18"/><error
to="a18"/></action>
+ <action name="a18"><fs><mkdir path='/tmp'/></fs><ok to="a19"/><error
to="a19"/></action>
+ <action name="a19"><fs><mkdir path='/tmp'/></fs><ok to="a20"/><error
to="a20"/></action>
+ <action name="a20"><fs><mkdir path='/tmp'/></fs><ok to="a21"/><error
to="a21"/></action>
+ <action name="a21"><fs><mkdir path='/tmp'/></fs><ok to="a22"/><error
to="a22"/></action>
+ <action name="a22"><fs><mkdir path='/tmp'/></fs><ok to="a23"/><error
to="a23"/></action>
+ <action name="a23"><fs><mkdir path='/tmp'/></fs><ok to="a24"/><error
to="a24"/></action>
+ <action name="a24"><fs><mkdir path='/tmp'/></fs><ok to="a25"/><error
to="a25"/></action>
+ <action name="a25"><fs><mkdir path='/tmp'/></fs><ok to="a26"/><error
to="a26"/></action>
+ <action name="a26"><fs><mkdir path='/tmp'/></fs><ok to="a27"/><error
to="a27"/></action>
+ <action name="a27"><fs><mkdir path='/tmp'/></fs><ok to="a28"/><error
to="a28"/></action>
+ <action name="a28"><fs><mkdir path='/tmp'/></fs><ok to="a29"/><error
to="a29"/></action>
+ <action name="a29"><fs><mkdir path='/tmp'/></fs><ok to="a30"/><error
to="a30"/></action>
+ <action name="a30"><fs><mkdir path='/tmp'/></fs><ok to="a31"/><error
to="a31"/></action>
+ <action name="a31"><fs><mkdir path='/tmp'/></fs><ok to="a32"/><error
to="a32"/></action>
+ <action name="a32"><fs><mkdir path='/tmp'/></fs><ok to="a33"/><error
to="a33"/></action>
+ <action name="a33"><fs><mkdir path='/tmp'/></fs><ok to="a34"/><error
to="a34"/></action>
+ <action name="a34"><fs><mkdir path='/tmp'/></fs><ok to="a35"/><error
to="a35"/></action>
+ <action name="a35"><fs><mkdir path='/tmp'/></fs><ok to="a36"/><error
to="a36"/></action>
+ <action name="a36"><fs><mkdir path='/tmp'/></fs><ok to="a37"/><error
to="a37"/></action>
+ <action name="a37"><fs><mkdir path='/tmp'/></fs><ok to="a38"/><error
to="a38"/></action>
+ <action name="a38"><fs><mkdir path='/tmp'/></fs><ok to="a39"/><error
to="a39"/></action>
+ <action name="a39"><fs><mkdir path='/tmp'/></fs><ok to="a40"/><error
to="a40"/></action>
+ <action name="a40"><fs><mkdir path='/tmp'/></fs><ok to="a41"/><error
to="a41"/></action>
+ <action name="a41"><fs><mkdir path='/tmp'/></fs><ok to="a42"/><error
to="a42"/></action>
+ <action name="a42"><fs><mkdir path='/tmp'/></fs><ok to="a43"/><error
to="a43"/></action>
+ <action name="a43"><fs><mkdir path='/tmp'/></fs><ok to="a44"/><error
to="a44"/></action>
+ <action name="a44"><fs><mkdir path='/tmp'/></fs><ok to="a45"/><error
to="a45"/></action>
+ <action name="a45"><fs><mkdir path='/tmp'/></fs><ok to="a46"/><error
to="a46"/></action>
+ <action name="a46"><fs><mkdir path='/tmp'/></fs><ok to="a47"/><error
to="a47"/></action>
+ <action name="a47"><fs><mkdir path='/tmp'/></fs><ok to="a48"/><error
to="a48"/></action>
+ <action name="a48"><fs><mkdir path='/tmp'/></fs><ok to="a49"/><error
to="a49"/></action>
+ <action name="a49"><fs><mkdir path='/tmp'/></fs><ok to="a50"/><error
to="a50"/></action>
+ <action name="a50"><fs><mkdir path='/tmp'/></fs><ok to="a51"/><error
to="a51"/></action>
+ <action name="a51"><fs><mkdir path='/tmp'/></fs><ok to="a52"/><error
to="a52"/></action>
+ <action name="a52"><fs><mkdir path='/tmp'/></fs><ok to="a53"/><error
to="a53"/></action>
+ <action name="a53"><fs><mkdir path='/tmp'/></fs><ok to="a54"/><error
to="a54"/></action>
+ <action name="a54"><fs><mkdir path='/tmp'/></fs><ok to="a55"/><error
to="a55"/></action>
+ <action name="a55"><fs><mkdir path='/tmp'/></fs><ok to="a56"/><error
to="a56"/></action>
+ <action name="a56"><fs><mkdir path='/tmp'/></fs><ok to="a57"/><error
to="a57"/></action>
+ <action name="a57"><fs><mkdir path='/tmp'/></fs><ok to="a58"/><error
to="a58"/></action>
+ <action name="a58"><fs><mkdir path='/tmp'/></fs><ok to="a59"/><error
to="a59"/></action>
+ <action name="a59"><fs><mkdir path='/tmp'/></fs><ok to="a60"/><error
to="a60"/></action>
+ <action name="a60"><fs><mkdir path='/tmp'/></fs><ok to="a61"/><error
to="a61"/></action>
+ <action name="a61"><fs><mkdir path='/tmp'/></fs><ok to="a62"/><error
to="a62"/></action>
+ <action name="a62"><fs><mkdir path='/tmp'/></fs><ok to="a63"/><error
to="a63"/></action>
+ <action name="a63"><fs><mkdir path='/tmp'/></fs><ok to="a64"/><error
to="a64"/></action>
+ <action name="a64"><fs><mkdir path='/tmp'/></fs><ok to="a65"/><error
to="a65"/></action>
+ <action name="a65"><fs><mkdir path='/tmp'/></fs><ok to="a66"/><error
to="a66"/></action>
+ <action name="a66"><fs><mkdir path='/tmp'/></fs><ok to="a67"/><error
to="a67"/></action>
+ <action name="a67"><fs><mkdir path='/tmp'/></fs><ok to="a68"/><error
to="a68"/></action>
+ <action name="a68"><fs><mkdir path='/tmp'/></fs><ok to="a69"/><error
to="a69"/></action>
+ <action name="a69"><fs><mkdir path='/tmp'/></fs><ok to="a70"/><error
to="a70"/></action>
+ <action name="a70"><fs><mkdir path='/tmp'/></fs><ok to="a71"/><error
to="a71"/></action>
+ <action name="a71"><fs><mkdir path='/tmp'/></fs><ok to="a72"/><error
to="a72"/></action>
+ <action name="a72"><fs><mkdir path='/tmp'/></fs><ok to="a73"/><error
to="a73"/></action>
+ <action name="a73"><fs><mkdir path='/tmp'/></fs><ok to="a74"/><error
to="a74"/></action>
+ <action name="a74"><fs><mkdir path='/tmp'/></fs><ok to="a75"/><error
to="a75"/></action>
+ <action name="a75"><fs><mkdir path='/tmp'/></fs><ok to="a76"/><error
to="a76"/></action>
+ <action name="a76"><fs><mkdir path='/tmp'/></fs><ok to="a77"/><error
to="a77"/></action>
+ <action name="a77"><fs><mkdir path='/tmp'/></fs><ok to="a78"/><error
to="a78"/></action>
+ <action name="a78"><fs><mkdir path='/tmp'/></fs><ok to="a79"/><error
to="a79"/></action>
+ <action name="a79"><fs><mkdir path='/tmp'/></fs><ok to="a80"/><error
to="a80"/></action>
+ <action name="a80"><fs><mkdir path='/tmp'/></fs><ok to="z"/><error
to="z"/></action>
+ <end name="z"/>
+</workflow-app>
\ No newline at end of file
diff --git a/release-log.txt b/release-log.txt
index caf5a08..e0c6329 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -1,5 +1,6 @@
-- Oozie 5.3.0 release (trunk - unreleased)
+OOZIE-3561 Forkjoin validation is slow when there are many actions in chain
(dionusos, pbacsko via asalamon74)
OOZIE-3491 Confusing System ID error message (matijhs via asalamon74)
OOZIE-3536 Invalid configuration tag <additionalparam> in maven-javadoc-plugin
(nobigo via asalamon74)
OOZIE-3559 Code generation encoding error in fluent-job-api (nobigo via
asalamon74)