Skip to content

fix: prevent stale tuples after a join - #2532

Open
triceo wants to merge 1 commit into
TimefoldAI:mainfrom
triceo:stale
Open

fix: prevent stale tuples after a join#2532
triceo wants to merge 1 commit into
TimefoldAI:mainfrom
triceo:stale

Conversation

@triceo

@triceo triceo commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

I fixed a left input variant of this a long time ago.
With the help of Claude, I was finally able to reproduce this from the right as well, and fixed it.

Copilot AI review requested due to automatic review settings July 26, 2026 11:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses correctness issues in Bavet join/ifExists processing where tuples can be observed “stale” (already retracting/inactive) when read from join storage, leading to invalid filtering evaluation and potentially incorrect intermediate tuple propagation.

Changes:

  • Add “from right” guarded insertion/update paths in AbstractJoinNode and use them when iterating right tuples from storage during left insert/update.
  • Update indexed and unindexed join nodes to avoid inserting out-tuples based on retracting right tuples.
  • Add regression tests covering the “stale right” scenario across join variants (indexed/unindexed, join/ifExists).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamTest.java Adds regression tests reproducing stale-right tuple scenarios for join/ifExists.
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractUnindexedJoinNode.java Uses guarded insertion when iterating right tuples from list storage during left insert.
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractJoinNode.java Introduces guarded “from right” insertion and update processing to skip inactive right tuples read from storage.
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIndexedJoinNode.java Uses guarded insertion when iterating right tuples from index storage during left propagation.
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIfExistsNode.java Skips updating counters from left when the right tuple is already retracting/inactive.

Copilot AI review requested due to automatic review settings July 26, 2026 11:50
@triceo triceo added this to the v2.5.0 milestone Jul 26, 2026
@triceo triceo self-assigned this Jul 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

@sonarqubecloud

Copy link
Copy Markdown

* Reachable when the right input's node sits in a higher layer than the left input's,
* which delivers the left's inserts before the right's retracts.
*/
protected final void insertOutTupleFilteredFromRight(LeftTuple_ leftTuple, UniTuple<Right_> rightTuple) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH not in love with this name; maybe insertOutTupleIfRightActiveFiltered?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants