Stackless Implementation Notes - mrietveld/jbpm GitHub Wiki
Wiki ▸ Stackless 1-Pager ▸ Stackless Implementaiton Notes
2014-05-09
Notes on the stackless implementation
In RunProcessInstance
, we have the following:
private final Stack<Deque<ProcessActionTrigger>> processActionQueueStack = new Stack<Deque<ProcessActionTrigger>>();
This field represents a stack of execution queues.
An execution queue represents one of 3 things
- The queue used for the ("normal") start of a process instance
- The queue added and used when an event is signalled
- The queue added and used when we enter an exception scope
The main reason for having a stack of queues (instead of just one queue) is that there are certain actions that always need to be taken at the end of an event being signalled, or after an exception scope has been handled.
If we try to solve these situations (signal-events, exception scopes) with just one queue, the logic to keep track of when the "end" or "clean-up" action should be taken becomes complex and ugly.
An easier way to understand this is the following: implementation vs execution.
- With the "old" recursive execution model: a lot of the implementation logic (such as, how to clean up after an fault node triggered an exception scope) was part of the execution logic.
For example:
- The
FaultNodeInstance
triggers, - .. which triggers the exception scope
- The exception scope does it thing
- and when the "stack" has returned, the
FaultNodeInstance
then calls the clean up code.
In this instance, because 2, 3, and 4, happen in sequence, and because the we have an execution model in which 3 does all sorts of things (instead of, for example, returning asynchronously), everything works.
However, what's "built-in" here is that 3 must always happen before 4. In other words, the exception scope must always have done it's things before 4 happens.
But that's a problem: what if we have a save-point, such as a human task? Well, that's our bug and to some extent one of the 3 big challenges with Compensation!
so we want to do this:
- The
FaultNodeInstance
triggers, - .. which triggers the exception scope
Questions / Challenges
- Mario is developing logic so that some
PropgationEntry
actions can be excuted in parallel.- We need to make sure that this does not happen when we have a timer (or async) job
that fires a
RunProcessInstance
action.
- We need to make sure that this does not happen when we have a timer (or async) job
that fires a
Stack trace
At the moment, an example of the current stack is this:
RuleFlowProcessInstance.internalStart(String) line: 35
* > NodeInstanceImpl.trigger(NodeInstance, String) line: 155
> StartNodeInstance.internalTrigger(NodeInstance, String) line: 43
> StartNodeInstance.triggerCompleted() line: 66
> StartNodeInstance(NodeInstanceImpl).triggerCompleted(String, boolean) line: 296
* > StartNodeInstance(NodeInstanceImpl).triggerNodeInstance(NodeInstance, String) line: 337
* > SplitInstance(NodeInstanceImpl).trigger(NodeInstance, String) line: 155
> SplitInstance.internalTrigger(NodeInstance, String) line: 63
> SplitInstance.executeStrategy(Split, String) line: 117
> SplitInstance(NodeInstanceImpl).triggerConnection(Connection) line: 352
* > SplitInstance(NodeInstanceImpl).triggerNodeInstance(NodeInstance, String) line: 327
* > ActionNodeInstance(NodeInstanceImpl.trigger(NodeInstance, String) line: 155
> ActionNodeInstance.internalTrigger(NodeInstance, String) line: 57
> ActionNodeInstance.triggerCompleted() line: 61
> ActionNodeInstance(NodeInstanceImpl).triggerCompleted(String, boolean) line: 296
* > ActionNodeInstance(NodeInstanceImpl).triggerNodeInstance(NodeInstance, String) line: 337
It looks like the best place to "snip" the stack is between
`NodeInstanceImpl.triggerNodeInstance(NodeInstance, String)`
and
`NodeInstanceImpl.trigger(NodeInstance, String)` methods.
Stack trace
The NodeInstanceImpl.triggerNodeInstance(NodeInstance, String)
method then returns the information
(next NodeInstance
, connection type String
) that is then used in the next 'loop' to trigger
that node instance.
The first "loop" of the process becomes this:
RuleFlowProcessInstance.internalStart(String) line: 35
> NodeInstanceImpl.trigger(NodeInstance, String) line: 155
> StartNodeInstance.internalTrigger(NodeInstance, String) line: 43
> StartNodeInstance.triggerCompleted() line: 66
> StartNodeInstance(NodeInstanceImpl).triggerCompleted(String, boolean) line: 296
> StartNodeInstance(NodeInstanceImpl).triggerNodeInstance(NodeInstance, String) line: 337
The NodeInstance
argument in the last triggerNodeInstance(NodeInstance, String)
method is
a SplitInstance
node instance. We use that information to trigger the following node instance,
which is then run as follow this (the NodeInstance
argument here is the previous node instance,
the StartNodeInstance
node instance.
> SplitInstance(NodeInstanceImpl).trigger(NodeInstance, String) line: 155
> SplitInstance.internalTrigger(NodeInstance, String) line: 63
> SplitInstance.executeStrategy(Split, String) line: 117
> SplitInstance(NodeInstanceImpl).triggerConnection(Connection) line: 352
> SplitInstance(NodeInstanceImpl).triggerNodeInstance(NodeInstance, String) line: 327
And so on:
> ActionNodeInstance(NodeInstanceImpl.trigger(NodeInstance, String) line: 155
> ActionNodeInstance.internalTrigger(NodeInstance, String) line: 57
> ActionNodeInstance.triggerCompleted() line: 61
> ActionNodeInstance(NodeInstanceImpl).triggerCompleted(String, boolean) line: 296
> ActionNodeInstance(NodeInstanceImpl).triggerNodeInstance(NodeInstance, String) line: 337
Check list
Issues
- Making sure all NodeInstance's do both recursive and queue-based
- Single-threaded vs multi-threaded implementations [: We leave this to the propagation queue]
- Marshalling / Save Points
- Make sure that this solution supports Compensation
Node Instances:
-
ActionNodeInstance
-
StartNodeInstance
-
EndNodeInstance
-
ThrowLinkNodeInstance
-
CatchLinkNodeInstance
-
FaultNodeInstance
-
SplitInstance
-
JoinInstance
-
ExtendedNodeInstanceImpl
-
EventNodeInstance
- AsyncEventNodeInstance
- BoundaryEventNodeInstance
-
StateBasedNodeInstance
- CompositeNodeInstance
-
CompositeNodeStartInstance
-
CompositeNodeEndInstance
-
CompositeContextNodeInstance
-
DynamicNodeInstance
-
EventSubProcessNodeInstance
-
ForEachNodeInstance
- ForEachJoinNodeInstance
- ForEachSplitNodeInstance
-
StateNodeInstance
-
-
MilestoneNodeInstance
-
RuleSetNodeInstance
-
SubProcessNodeInstance
-
TimerNodeInstance
-
WorkItemNodeInstance
- HumanTaskNodeInstance
-
- CompositeNodeInstance
-