You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by "Copilot (via GitHub)" <gi...@apache.org> on 2026/09/02 10:14:15 UTC
Re: [PR] feat(sequencing): introduce sequence() DSL and Phreak SequenceNode [incubator-kie]
Copilot commented on code in PR #6909:
URL: https://github.com/apache/incubator-kie/pull/6909#discussion_r3913036674
##########
drools-base/src/main/java/org/drools/base/rule/constraint/CombinedAlphaConstraint.java:
##########
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+package org.drools.base.rule.constraint;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Arrays;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.rule.Declaration;
+import org.kie.api.runtime.rule.FactHandle;
+
+/**
+ * ANDs two or more {@link AlphaNodeFieldConstraint}s into a single constraint.
+ * Used by the sequencing runtime when a sequence step carries multiple expressions:
+ * every expression must be satisfied for the step to match.
+ */
+public class CombinedAlphaConstraint implements AlphaNodeFieldConstraint {
+
+ private AlphaNodeFieldConstraint[] constraints;
+
+ public CombinedAlphaConstraint(AlphaNodeFieldConstraint[] constraints) {
+ this.constraints = constraints;
+ }
Review Comment:
This type implements `Externalizable` through `Constraint` and defines `readExternal`, but it has no public no-argument constructor. Java deserialization therefore fails with `InvalidClassException` before `readExternal` can run. Add the required constructor.
##########
drools-base/src/main/java/org/drools/base/reteoo/sequencing/Sequence.java:
##########
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ */
+package org.drools.base.reteoo.sequencing;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.DynamicFilter;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGate;
+import org.drools.base.reteoo.sequencing.steps.LogicCircuitStep;
+import org.drools.base.reteoo.sequencing.steps.Step;
+import org.drools.base.reteoo.sequencing.steps.Step.StepFactory;
+import org.drools.base.rule.Declaration;
+import org.drools.base.rule.Pattern;
+import org.drools.base.rule.RuleConditionElement;
+import org.drools.base.reteoo.SignalAdapter;
+import org.drools.base.util.CircularArrayList;
+import org.kie.api.runtime.rule.FactHandle;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class Sequence implements RuleConditionElement {
+ private final int sequenceIndex;
+
+ private Pattern[] filters;
+
+ private Step[] steps;
+
+ private LogicGate[] gates;
+
+ private SequenceController controller;
+
+ public Sequence(int sequenceIndex, StepFactory... stepFactories) {
+ this.steps = new Step[stepFactories.length];
+ this.sequenceIndex = sequenceIndex;
+ this.controller = DefaultController.getInstance();
+
+ for ( int i = 0; i < steps.length; i++ ) {
+ steps[i] = stepFactories[i].createStep(this);
+ }
+ populateLogicGates();
+ }
+
+ public Pattern[] getFilters() {
+ return filters;
+ }
+
+ public void setFilters(Pattern[] filters) {
+ this.filters = filters;
+ }
+
+ public void populateLogicGates() {
+ List<LogicGate> list = new ArrayList<>();
+ for (Step step : getSteps()) {
+ if (step instanceof LogicCircuitStep) {
+ LogicGate[] circuitGates = ((LogicCircuitStep) step).getCircuit().getGates();
+ for (int i = 0; i < circuitGates.length; i++) {
+ list.add(circuitGates[i]);
+ }
+ }
+ }
+ gates = list.toArray(new LogicGate[0]);
+ }
+
+ public int getSequenceIndex() {
+ return sequenceIndex;
+ }
+
+ public Step[] getSteps() {
+ return steps;
+ }
+
+ public void setSteps(Step[] steps) {
+ this.steps = steps;
+ }
+
+ public LogicGate[] getGates() {
+ return gates;
+ }
+
+ @Override
+ public Map<String, Declaration> getInnerDeclarations() {
+ return Map.of();
+ }
+
+ @Override
+ public Map<String, Declaration> getOuterDeclarations() {
+ return Map.of();
+ }
+
+ @Override
+ public Declaration resolveDeclaration(String identifier) {
+ return null;
+ }
+
+ @Override
+ public RuleConditionElement clone() {
+ Sequence clone = new Sequence(sequenceIndex);
+ clone.steps = this.steps;
+ clone.gates = this.gates;
+ clone.filters = this.filters;
+ clone.controller = this.controller;
+ return clone;
+ }
+
+ @Override
+ public List<? extends RuleConditionElement> getNestedElements() {
+ return List.of();
+ }
+
+ @Override
+ public boolean isPatternScopeDelimiter() {
+ return false;
+ }
+
+ @Override
+ public void writeExternal(ObjectOutput out) throws IOException {
+ throw new UnsupportedOperationException("Sequence does not support serialization");
Review Comment:
`Sequence` is a `RuleConditionElement`, hence `Externalizable`; throwing here makes serialization of every package or KieBase containing `sequence()` fail. Implement round-trip serialization for the sequence descriptors (and their step/gate graph) rather than making the new condition unusable in serialized deployments.
##########
drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/KiePackagesBuilder.java:
##########
@@ -398,15 +407,41 @@ private void processConsequence( RuleContext ctx, Consequence consequence, Strin
Declaration[] requiredDeclarations = getRequiredDeclarationsIfPossible( ctx, consequence, name );
boolean enabledTupleOptimization = requiredDeclarations != null && requiredDeclarations.length > 0;
+ Map<String, int[]> sequenceVarIndexes = buildSequenceVarIndexesForConsequence( ctx, consequence );
+
if ( name.equals( RuleImpl.DEFAULT_CONSEQUENCE_NAME ) ) {
if ("java".equals(consequence.getLanguage())) {
- ctx.getRule().setConsequence( new LambdaConsequence( consequence, enabledTupleOptimization ) );
+ ctx.getRule().setConsequence( new LambdaConsequence( consequence, enabledTupleOptimization, sequenceVarIndexes ) );
} else {
throw new UnsupportedOperationException("Unknown script language for consequence: " + consequence.getLanguage());
}
} else {
- ctx.getRule().addNamedConsequence( name, new LambdaConsequence( consequence, enabledTupleOptimization ) );
+ ctx.getRule().addNamedConsequence( name, new LambdaConsequence( consequence, enabledTupleOptimization, sequenceVarIndexes ) );
+ }
+ }
+
+ /**
+ * Builds a map of variable-name → sequencer-data-index for any consequence variables
+ * that were bound by a sequence step pattern (rather than a normal LHS pattern).
+ * These variables are not accessible via the activation tuple; they must be resolved
+ * from SequencerMemory.getData() at consequence evaluation time.
+ */
+ private Map<String, int[]> buildSequenceVarIndexesForConsequence( RuleContext ctx, Consequence consequence ) {
+ Map<Variable, int[]> seqIndexes = ctx.getSequenceVarIndexes();
+ if (seqIndexes.isEmpty()) {
+ return Map.of();
+ }
+ int totalSeqs = ctx.getSequenceCount();
+ Map<String, int[]> result = null;
+ for (Variable var : consequence.getDeclarations()) {
+ int[] entry = seqIndexes.get(var);
+ if (entry != null) {
+ if (result == null) result = new java.util.HashMap<>();
+ int skip = totalSeqs - 1 - entry[RuleContext.SEQ_VAR_SEQ_IDX];
+ result.put(var.getName(), new int[]{ skip, entry[RuleContext.SEQ_VAR_FILTER_IDX] });
Review Comment:
This skip count assumes every consequence terminal is after all sequences in the rule. A named consequence placed after sequence 1 but before sequence 2 has only sequence 1's anchor in its tuple chain, yet `totalSeqs` produces `skip == 1`; `findSequencerAnchor` then fails. Compute skips relative to the sequence nodes that precede each specific consequence terminal, not the rule-wide sequence count.
##########
drools-core/src/main/java/org/drools/core/reteoo/builder/PhreakNodeFactory.java:
##########
@@ -113,6 +128,83 @@ public TupleToObjectNode buildRightInputNode(int id, LeftTupleSource leftInput,
}
return new TupleToObjectNode(id, leftInput, startTupleSource, context );
}
+ public SequenceNode buildSequenceNode(final int id,
+ final LeftTupleSource tupleSource,
+ final Sequence seq,
+ final BuildContext context) {
+ SequenceNode node = new SequenceNode(id, tupleSource, context);
+ node.setSequencer(new Sequencer(seq));
+
+ NodeFactory nFactory = CoreComponentFactory.get().getNodeFactoryService();
+
+ Pattern[] patterns = seq.getFilters();
+
+ // first attach the OTN sources in Rete.
+ // Use LinkedHashSet to preserve insertion order (first-seen per type),
+ // so objectTypeList index aligns with the first pattern index for that type.
+ Set<ObjectType> objectTypeSet = new LinkedHashSet<>();
+ Arrays.stream(patterns).forEach(p -> objectTypeSet.add(p.getObjectType()));
+ List<ObjectType> objectTypeList = new ArrayList<>(objectTypeSet);
+
+ Map<ObjectType, Integer> objectTypeIndex = new HashMap<>(objectTypeList.size() * 2);
+ ObjectSource[] otns = new ObjectSource[objectTypeList.size()];
+ EntryPointNode epn = context.getRuleBase().getRete().getEntryPointNode(context.getCurrentEntryPoint());
+ Map<ObjectType, ObjectTypeNode> existingOtns = epn.getObjectTypeNodes();
+ for ( int i = 0; i < objectTypeList.size(); i++ ) {
+ ObjectType objectType = objectTypeList.get(i);
+ objectTypeIndex.put(objectType, i);
+ ObjectTypeNode existing = existingOtns.get(objectType);
+ if (existing != null) {
+ // Reuse the existing OTN — building a fresh one and calling attach()
+ // would call EntryPointNode.addObjectSink which is a Map.put, silently
+ // replacing the existing OTN and cutting off all rules already wired to it.
+ otns[i] = existing;
+ } else {
+ otns[i] = nFactory.buildObjectTypeNode(context.getNextNodeId(), epn, objectType, context);
+ otns[i].attach(context);
+ }
+ }
+
+ // For each source create an alpha adapter, which will map it to any filters on the stack.
+ AlphaAdapter[] adapters = new AlphaAdapter[objectTypeList.size()];
+ for ( int i = 0; i < objectTypeList.size(); i++ ) {
+ adapters[i] = new AlphaAdapter(context.getNextNodeId(), otns[i],
+ context.getPartitionId(), node, i);
+
+ adapters[i].attach(context);
+ }
+ node.setAlphaAdapters(adapters);
+
+
+ // Create all the dynamic filters, these are added on demand to the stack.
+ // filterIndex (i) is the pattern index — one per step.
+ // adapterIndex is the index of this pattern's type in objectTypeList — one per distinct type.
+ DynamicFilterProto[] filters = new DynamicFilterProto[patterns.length];
+ for ( int i = 0; i < patterns.length; i++ ) {
+ List<Constraint> constraints = patterns[i].getConstraints();
+ if (constraints.isEmpty()) {
+ throw new IllegalArgumentException(
+ "sequence step " + i + " has no constraint expression. " +
+ "Every sequence step must have at least one expr(). " +
+ "Add an expr() to the pattern, or use a dedicated fact type per step.");
+ }
+ AlphaNodeFieldConstraint combined;
+ if (constraints.size() == 1) {
+ combined = (AlphaNodeFieldConstraint) constraints.get(0);
Review Comment:
A sequence `PatternDef` can legally contain a beta expression referencing the anchor or another declaration, but this unchecked cast then produces a `ClassCastException` during KieBase construction. Either support beta constraints for sequence steps or explicitly validate the constraint type and report the alpha-only limitation with a descriptive build error.
##########
drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/consequence/LambdaConsequence.java:
##########
@@ -33,28 +34,42 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
public class LambdaConsequence implements Consequence<KnowledgeHelper> {
// Enable the optimization to extract from the activation tuple the arguments to be passed to this
// consequence in linear time by traversing the tuple only once.
private static final boolean ENABLE_LINEARIZED_ARGUMENTS_RETRIEVAL_OPTIMIZATION = true;
+ /** Index into the {@code int[]} values of {@link #sequenceVarIndexes}: tuple-anchor skip count. */
+ private static final int SEQ_ENTRY_SKIP_IDX = 0;
+ /** Index into the {@code int[]} values of {@link #sequenceVarIndexes}: filter/step index within the sequence. */
+ private static final int SEQ_ENTRY_FILTER_IDX = 1;
+
private final org.drools.model.Consequence consequence;
private final int factsNr;
private final boolean enabledTupleOptimization;
private Declaration[] requiredDeclarations;
+ // variable name → [skip, filterIndex] for step-pattern variables.
+ // Non-empty only when the rule contains a sequence() and the consequence references step-bound vars.
+ private final Map<String, int[]> sequenceVarIndexes;
Review Comment:
Sequence-bound facts are resolved only by the optimized `initConsequence` path. When a rule also has a first-level OR, `enabledTupleOptimization` is false and `evaluate` uses `declarationsToFacts`, which reads every fact from the activation tuple; sequence-step facts are not in that tuple, so an RHS binding from `sequence()` fails or resolves the wrong handle. The non-optimized path must also consult these indexes and resolve sequence variables from `SequencerMemory`.
##########
drools-ruleunits/drools-ruleunits-impl/src/main/java/org/drools/ruleunits/impl/sessions/RuleUnitExecutorImpl.java:
##########
@@ -22,6 +22,7 @@
import org.drools.base.definitions.rule.impl.RuleImpl;
import org.drools.base.factmodel.traits.Thing;
import org.drools.base.factmodel.traits.TraitableBean;
+import org.drools.base.phreak.PropagationEntry;
Review Comment:
`PropagationEntry` is already imported at line 57, so this addition leaves a duplicate import and will be rejected by redundant-import/style checks. Keep only one import.
##########
drools-core/src/main/java/org/drools/core/reteoo/SequenceNode.java:
##########
@@ -0,0 +1,588 @@
+/*
+ * 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.
+ */
+package org.drools.core.reteoo;
+
+import org.drools.base.base.ObjectType;
+import org.drools.base.common.RuleBasePartitionId;
+import org.drools.base.reteoo.DynamicFilter;
+import org.drools.base.reteoo.DynamicFilterProto;
+import org.drools.base.reteoo.NodeTypeEnums;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+import org.drools.base.rule.Pattern;
+import org.drools.core.RuleBaseConfiguration;
+import org.drools.core.common.InternalFactHandle;
+import org.drools.core.common.InternalWorkingMemory;
+import org.drools.core.common.Memory;
+import org.drools.core.common.MemoryFactory;
+import org.drools.core.common.PropagationContext;
+import org.drools.core.common.ReteEvaluator;
+import org.drools.core.common.TupleSets;
+import org.drools.core.common.TupleSetsImpl;
+import org.drools.core.common.UpdateContext;
+import org.drools.core.reteoo.SequenceNode.SequenceNodeMemory;
+import org.drools.core.reteoo.builder.BuildContext;
+import org.drools.base.reteoo.sequencing.Sequencer;
+import org.drools.core.reteoo.sequencing.SequencerMemoryImpl;
+import org.drools.base.reteoo.sequencing.SequencerMemory;
+import org.drools.base.util.AbstractLinkedListNode;
+import org.drools.base.util.LinkedList;
+import org.drools.core.util.index.TupleList;
+import org.drools.util.bitmask.BitMask;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.List;
+
+public class SequenceNode extends LeftTupleSource
+ implements
+ LeftTupleNode,
+ LeftTupleSinkNode,
+ MemoryFactory<SequenceNodeMemory> {
+
+ private static final long serialVersionUID = 510l;
+
+ protected boolean tupleMemoryEnabled;
+
+ private Sequencer sequencer;
+
+ private AlphaAdapter[] alphaAdapters;
+
+ private DynamicFilterProto[] dynamicFilters;
+
+ private LeftTupleSinkNode previousTupleSinkNode;
+
+ private LeftTupleSinkNode nextTupleSinkNode;
+
+ public SequenceNode() {
+
+ }
+
+ public SequenceNode(final int id,
+ final LeftTupleSource tupleSource,
+ final BuildContext context) {
+ super(id, context);
+ setLeftTupleSource(tupleSource);
+ this.tupleMemoryEnabled = context.isTupleMemoryEnabled();
+
+ initMasks(context);
+
+ hashcode = calculateHashCode();
+ setStreamMode(true);
+ }
+
+ public void setAlphaAdapters(AlphaAdapter[] adapters) {
+ this.alphaAdapters = adapters;
+ }
+
+ public DynamicFilterProto[] getDynamicFilters() {
+ return dynamicFilters;
+ }
+
+ public void setDynamicFilters(DynamicFilterProto[] dynamicFilters) {
+ this.dynamicFilters = dynamicFilters;
+ }
+
+ public void doAttach(BuildContext context) {
+ super.doAttach(context);
+ this.leftInput.addTupleSink( this, context );
+ }
+
+ public void networkUpdated(UpdateContext updateContext) {
+ this.leftInput.networkUpdated(updateContext);
+ }
+
+
+ /**
+ * Produce a debug string.
+ *
+ * @return The debug string.
+ */
+ public String toString() {
+ return "[SequenceNode(" + this.id + ")]]";
+ }
+
+ private int calculateHashCode() {
+ int result = this.leftInput.hashCode();
+ if (this.sequencer != null) {
+ result = 31 * result + System.identityHashCode(this.sequencer.getSequence());
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (this == object) {
+ return true;
+ }
+
+ if ( object == null || !(object instanceof SequenceNode) || this.hashCode() != object.hashCode() ) {
+ return false;
+ }
+
+ SequenceNode other = (SequenceNode) object;
+ return this.leftInput.getId() == other.leftInput.getId()
+ && this.sequencer.getSequence() == other.sequencer.getSequence();
+ }
+
+ public SequenceNodeMemory createMemory(final RuleBaseConfiguration config, ReteEvaluator reteEvaluator) {
+ // activeFilters is indexed by adapter index (one slot per distinct object type).
+ // AlphaAdapter.assertObject looks up activeFilters[adapterIndex] to dispatch to matching filters.
+ // filters is indexed by pattern/filter index (one slot per step).
+ // The two sizes differ when multiple steps share the same object type.
+ LinkedList<DynamicFilter>[] activeFilters = new LinkedList[alphaAdapters.length];
+ SequenceNodeMemory memory = new SequenceNodeMemory(this, activeFilters, new DynamicFilter[dynamicFilters.length]);
+ return memory;
+ }
+
+ public SequencerMemory createSequencerMemory(TupleImpl lt, LeftTupleSink sink, SequenceNodeMemory nodeMemory) {
+ SequencerMemory sequencerMemory = new SequencerMemoryImpl(sequencer, lt, sink, this, nodeMemory);
+
+ return sequencerMemory;
+ }
+
+ public Sequencer getSequencer() {
+ return sequencer;
+ }
+
+ public void setSequencer(Sequencer sequencer) {
+ this.sequencer = sequencer;
+ // hashcode must include the Sequencer's Sequence so that two SequenceNodes
+ // on the same left-input but with different sequences are never merged by
+ // the node-sharing mechanism. Recomputed here because setSequencer() is
+ // called after construction (in PhreakNodeFactory.buildSequenceNode).
+ this.hashcode = calculateHashCode();
+ }
+
+ public boolean isLeftTupleMemoryEnabled() {
+ return tupleMemoryEnabled;
+ }
+
+ /**
+ * Returns the next node
+ * @return
+ * The next TupleSinkNode
+ */
+ public LeftTupleSinkNode getNextLeftTupleSinkNode() {
+ return this.nextTupleSinkNode;
+ }
+
+ /**
+ * Sets the next node
+ * @param next
+ * The next TupleSinkNode
+ */
+ public void setNextLeftTupleSinkNode(final LeftTupleSinkNode next) {
+ this.nextTupleSinkNode = next;
+ }
+
+ /**
+ * Returns the previous node
+ * @return
+ * The previous TupleSinkNode
+ */
+ public LeftTupleSinkNode getPreviousLeftTupleSinkNode() {
+ return this.previousTupleSinkNode;
+ }
+
+ /**
+ * Sets the previous node
+ * @param previous
+ * The previous TupleSinkNode
+ */
+ public void setPreviousLeftTupleSinkNode(final LeftTupleSinkNode previous) {
+ this.previousTupleSinkNode = previous;
+ }
+
+ public int getType() {
+ return NodeTypeEnums.SequenceNode;
+ }
+
+ public static class SequenceNodeMemory extends AbstractLinkedListNode<Memory>
+ implements
+ Externalizable,
+ Memory {
+
+ private static final long serialVersionUID = 510l;
+
+ private TupleMemory leftTupleMemory;
+
+ private TupleSets stagedChildTuples;
+
+ private SegmentMemory memory;
+
+ private LinkedList<DynamicFilter>[] activeFilters;
+
+ private DynamicFilter[] filters;
+
+ private SequenceNode node;
+
+ private long nodePosMaskBit;
+
+ public SequenceNodeMemory(SequenceNode node, LinkedList<DynamicFilter>[] activeFilters, DynamicFilter[] filters) {
+ this.node = node;
+ this.stagedChildTuples = new TupleSetsImpl();
+ this.leftTupleMemory = new TupleList();
+ this.activeFilters = activeFilters;
+ this.filters = filters;
+ }
+
+ public void addActiveFilter(DynamicFilter filter) {
+ int adapterIdx = filter.getAdapterIndex();
+ if (this.activeFilters[adapterIdx] == null) {
+ this.activeFilters[adapterIdx] = new LinkedList<>();
+ }
+ this.activeFilters[adapterIdx].add(filter);
+ }
+
+ public void removeActiveFilter(DynamicFilter filter) {
+ this.activeFilters[filter.getAdapterIndex()].remove(filter);
+ }
+
+ public LinkedList<DynamicFilter>[] getActiveFilters() {
+ return this.activeFilters;
+ }
+
+ public DynamicFilter[] getFilters() {
+ return filters;
+ }
+
+ public void setFilters(DynamicFilter[] filters) {
+ this.filters = filters;
+ }
+
+ public TupleMemory getLeftTupleMemory() {
+ return leftTupleMemory;
+ }
+
+ public TupleSets getStagedChildTuples() {
+ return stagedChildTuples;
+ }
+
+ public SegmentMemory getMemory() {
+ return memory;
+ }
+
+ public void readExternal(ObjectInput in) throws IOException,
+ ClassNotFoundException {
+ throw new UnsupportedOperationException("SequenceNodeMemory does not support serialization");
+ }
+
+ public void writeExternal(ObjectOutput out) throws IOException {
+ throw new UnsupportedOperationException("SequenceNodeMemory does not support serialization");
+ }
+
+ public int getNodeType() {
+ return NodeTypeEnums.SequenceNode;
+ }
+
+ public void setSegmentMemory(SegmentMemory smem) {
+ this.memory = smem;
+ }
+
+ public SegmentMemory getSegmentMemory() {
+ return this.memory;
+ }
+
+ public void reset() { }
Review Comment:
Session pooling calls `NodeMemories.resetAllMemories()`, but this no-op retains the old left tuples, staged children, dynamic filters, and signal adapters. Reusing a pooled session can consequently route new step events into sequencers from the previous session and retain old facts. Reset all tuple/filter state here (and deactivate/clear active adapters) before the session is reused.
##########
drools-core/src/main/java/org/drools/core/reteoo/SequenceNode.java:
##########
@@ -0,0 +1,588 @@
+/*
+ * 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.
+ */
+package org.drools.core.reteoo;
+
+import org.drools.base.base.ObjectType;
+import org.drools.base.common.RuleBasePartitionId;
+import org.drools.base.reteoo.DynamicFilter;
+import org.drools.base.reteoo.DynamicFilterProto;
+import org.drools.base.reteoo.NodeTypeEnums;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+import org.drools.base.rule.Pattern;
+import org.drools.core.RuleBaseConfiguration;
+import org.drools.core.common.InternalFactHandle;
+import org.drools.core.common.InternalWorkingMemory;
+import org.drools.core.common.Memory;
+import org.drools.core.common.MemoryFactory;
+import org.drools.core.common.PropagationContext;
+import org.drools.core.common.ReteEvaluator;
+import org.drools.core.common.TupleSets;
+import org.drools.core.common.TupleSetsImpl;
+import org.drools.core.common.UpdateContext;
+import org.drools.core.reteoo.SequenceNode.SequenceNodeMemory;
+import org.drools.core.reteoo.builder.BuildContext;
+import org.drools.base.reteoo.sequencing.Sequencer;
+import org.drools.core.reteoo.sequencing.SequencerMemoryImpl;
+import org.drools.base.reteoo.sequencing.SequencerMemory;
+import org.drools.base.util.AbstractLinkedListNode;
+import org.drools.base.util.LinkedList;
+import org.drools.core.util.index.TupleList;
+import org.drools.util.bitmask.BitMask;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.List;
+
+public class SequenceNode extends LeftTupleSource
+ implements
+ LeftTupleNode,
+ LeftTupleSinkNode,
+ MemoryFactory<SequenceNodeMemory> {
+
+ private static final long serialVersionUID = 510l;
+
+ protected boolean tupleMemoryEnabled;
+
+ private Sequencer sequencer;
+
+ private AlphaAdapter[] alphaAdapters;
+
+ private DynamicFilterProto[] dynamicFilters;
+
+ private LeftTupleSinkNode previousTupleSinkNode;
+
+ private LeftTupleSinkNode nextTupleSinkNode;
+
+ public SequenceNode() {
+
+ }
+
+ public SequenceNode(final int id,
+ final LeftTupleSource tupleSource,
+ final BuildContext context) {
+ super(id, context);
+ setLeftTupleSource(tupleSource);
+ this.tupleMemoryEnabled = context.isTupleMemoryEnabled();
Review Comment:
`SequenceNode` does not contribute a fact to the tuple, but unlike every other non-fact `LeftTupleSource` it leaves `objectCount` at its default zero. Downstream nodes therefore inherit the wrong tuple width (for example, a join after an anchored sequence counts only its own fact), which can corrupt declaration/tuple indexing. Copy the count from `leftInput`, as `EvalConditionNode` and `TimerNode` do.
--
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: commits-unsubscribe@kie.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org