Skip to content

fix(pipeline): clear watch state after successful EXEC - #1498

Open
seidelmartin wants to merge 3 commits into
stipsan:mainfrom
seidelmartin:fix/pipeline-clear-watch-state-after-exec
Open

fix(pipeline): clear watch state after successful EXEC#1498
seidelmartin wants to merge 3 commits into
stipsan:mainfrom
seidelmartin:fix/pipeline-clear-watch-state-after-exec

Conversation

@seidelmartin

@seidelmartin seidelmartin commented Jul 21, 2026

Copy link
Copy Markdown

Summary

  • Pipeline.exec() had an asymmetric implementation: the abort path (dirty/expired watched key) correctly cleared redis.watching and redis.dirty, but the success path did not — causing the pipeline's own commands to re-dirty the client via _signalModifiedKey, making all subsequent MULTI/EXEC pipelines incorrectly return null.
  • In real Redis, EXEC always clears watch state regardless of outcome. However, only MULTI/EXEC interacts with watch semantics — regular pipelines (redis.pipeline()) should run unconditionally without checking or clearing watch state.
  • Introduced an _isMulti flag (set by redis.multi()) to distinguish the two cases, and scoped both the dirty abort check and watch-state clearing in exec() to MULTI transactions only.

Test plan

  • Added regression test: should clear watch state after a successful exec so subsequent pipelines are not aborted — verifies MULTI/EXEC clears watch state for subsequent transactions
  • Added regression test: should not clear watch state after a regular pipeline exec — verifies WATCH + regular pipeline + MULTI/EXEC correctly aborts when a watched key is modified
  • Verified each test fails before the fix and passes after
  • Full test suite passes (1471 tests, 0 regressions)

🤖 Generated with Claude Code

Real Redis always clears watching keys and dirty flag after EXEC, regardless
of whether the transaction succeeded or was aborted. The abort path already
did this, but the success path did not — causing the pipeline's own commands
to re-dirty the client via modifiedKeyEvents, making subsequent pipelines
incorrectly return null.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 07:54

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 fixes transaction (WATCH/MULTI/EXEC) state handling in ioredis-mock by ensuring the watch/dirty state is cleared on a successful EXEC, matching real Redis behavior and preventing subsequent transactions on the same connection from being incorrectly aborted.

Changes:

  • Clear redis.watching and reset redis.dirty on the successful Pipeline.exec() path.
  • Add an integration regression test to verify a second MULTI/EXEC succeeds after a successful first EXEC.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/pipeline.js Clears watch/dirty state on successful exec() to prevent re-dirtying during execution and subsequent false aborts.
test/integration/exec.js Adds regression coverage to ensure subsequent MULTI/EXEC calls are not aborted after a successful EXEC.

Comment thread src/pipeline.js Outdated
Comment on lines +100 to +103
// Real Redis always clears watch state after EXEC, whether it succeeded or aborted.
// Clear before running batch so commands within the pipeline don't re-dirty the state.
this.redis.watching.clear()
this.redis.dirty = false

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch. Fixed in 1ee6189 — added an _isMulti flag set by redis.multi(), and scoped both the _isDirty() abort check and the watch-state clearing in exec() to MULTI transactions only. Regular pipelines now run unconditionally without touching watch state. Added a regression test covering the WATCH + pipeline + MULTI/EXEC scenario.

Regular pipelines (redis.pipeline()) should not check or clear WATCH state —
only MULTI transactions (redis.multi()) interact with watch semantics in real
Redis. Introduce _isMulti flag so exec() can distinguish the two cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 08:13
@changeset-bot

changeset-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e4651ec

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
ioredis-mock Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

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 4 out of 4 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 21, 2026 08:17

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 4 out of 4 changed files in this pull request and generated no new comments.

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.

2 participants