Thebas opened a new issue, #2406:
URL: https://github.com/apache/incubator-kie-issues/issues/2406
### Component
Drools
### Versions
- Drools: 10.2.0
- Java: 21 runtime (locally 21.0.2); Maven compilation target 17
- Spring Boot: 3.5.16
- Maven: 3.9.16 (locally installed)
### Problem
`PhreakFromNode.doLeftDeletes` removes the staged left tuple from the from
node's
left tuple memory unconditionally:
```java
for (TupleImpl leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple !=
null; ) {
TupleImpl next = leftTuple.getStagedNext();
ltm.remove(leftTuple);
```
When the tuple is not in that memory — it was staged and never actually
added, or
it was already removed — org.drools.base.util.LinkedList.remove() misses
both the
firstNode and lastNode branches, falls into the middle-node branch and
dereferences a null previous:
java.lang.NullPointerException: Cannot invoke
"org.drools.core.util.DoubleLinkedEntry.setNext(org.drools.core.util.SingleLinkedEntry)"
because the return value of
"org.drools.core.util.DoubleLinkedEntry.getPrevious()" is null
at org.drools.core.util.LinkedList.remove(LinkedList.java:178)
at org.drools.core.util.index.TupleList.remove(TupleList.java:51)
at
org.drools.core.phreak.PhreakFromNode.doLeftDeletes(PhreakFromNode.java:207)
at org.drools.core.phreak.PhreakFromNode.doNode(PhreakFromNode.java:64)
at
org.drools.core.phreak.RuleNetworkEvaluatorImpl.evaluateNonBetaNonTerminalNode(RuleNetworkEvaluatorImpl.java:405)
at
org.drools.core.phreak.RuleNetworkEvaluatorImpl.evaluateNonTerminalNode(RuleNetworkEvaluatorImpl.java:385)
at
org.drools.core.phreak.RuleNetworkEvaluatorImpl.innerEval(RuleNetworkEvaluatorImpl.java:318)
at
org.drools.core.phreak.RuleNetworkEvaluatorImpl.outerEval(RuleNetworkEvaluatorImpl.java:250)
at
org.drools.core.phreak.RuleNetworkEvaluatorImpl.evaluateNetwork(RuleNetworkEvaluatorImpl.java:142)
at
org.drools.core.phreak.RuleExecutor.evaluateNetwork(RuleExecutor.java:225)
at
org.drools.core.phreak.RuleExecutor.evaluateNetworkIfDirty(RuleExecutor.java:236)
at
org.drools.core.phreak.RuleExecutor.evaluateNetworkAndFire(RuleExecutor.java:85)
at
org.drools.core.concurrent.AbstractGroupEvaluator.evaluateAndFire(AbstractGroupEvaluator.java:53)
at
org.drools.kiesession.agenda.DefaultAgenda.fireLoop(DefaultAgenda.java:616)
at
org.drools.kiesession.agenda.DefaultAgenda.internalFireAllRules(DefaultAgenda.java:569)
at
org.drools.kiesession.agenda.DefaultAgenda.fireAllRules(DefaultAgenda.java:561)
at
org.drools.kiesession.session.StatefulKnowledgeSessionImpl.internalFireAllRules(StatefulKnowledgeSessionImpl.java:1110)
at
org.drools.kiesession.session.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1101)
Seen in production on a stateful KieSession driving fireAllRules() from a
Kafka
listener, with rules using from. The session is serialised behind a write
lock, so
the insert/fire path is single threaded.
PhreakFromNode is the only Phreak node that does not guard this call. Every
other
one does, with the same comment:
┌───────────────────────────────────┐
│ Node │
Guard │
├───────────────────────────────────┤
│ PhreakJoinNode:409 │ if
(leftTuple.getMemory() != null) │
├───────────────────────────────────┤
│ PhreakNotNode:446 │ if
(leftTuple.getMemory() != null) │
├───────────────────────────────────┤
│ PhreakExistsNode:432 │ if
(leftTuple.getMemory() != null) │
├───────────────────────────────────┤
│ PhreakAccumulateNode:556 │ if (leftTuple.getMemory() !=
null) │
├───────────────────────────────────┤
│ PhreakSubnetworkNotExistsNode:193 │ if (leftTuple.getMemory() != null) │
├───────────────────────────────────┤
│ PhreakNodeOperations:219, 240 │ if (leftTuple.getMemory() !=
null) │
├───────────────────────────────────┤
│ PhreakFromNode:207 │ missing
│
└───────────────────────────────────┘
PhreakReactiveFromNode extends PhreakFromNode and does not override
doLeftDeletes, so it has the same gap.
Expected behaviour
Deleting a left tuple that is not in the from node's left tuple memory is a
no-op, as
it is on every other Phreak node — not an NPE that aborts fireAllRules() and
leaves
the session in an undefined state.
Related
RuleExecutorDormantTupleTest / issue #6422 is the same failure mode
(LinkedList.remove on a tuple that is already unlinked) in RuleExecutor.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]