Skip to content

Add retries for idempotent errors on Session.run() - #1404

Merged
MaxAake merged 17 commits into
6.xfrom
idempotent-retries
May 26, 2026
Merged

Add retries for idempotent errors on Session.run()#1404
MaxAake merged 17 commits into
6.xfrom
idempotent-retries

Conversation

@MaxAake

@MaxAake MaxAake commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Closes: DRIVERS-309

@MaxAake
MaxAake force-pushed the idempotent-retries branch from 16109e5 to 68dfa92 Compare April 16, 2026 06:36
@MaxAake
MaxAake force-pushed the idempotent-retries branch from 2904c5c to 34c25a9 Compare May 11, 2026 11:05
@MaxAake
MaxAake marked this pull request as ready for review May 12, 2026 08:58
Comment thread packages/core/src/result.ts Outdated
}).catch(onErrorOriginal)
const onErrorWrapper = (error: Error, runError?: boolean): void => {
if (
runError === true && !this._hasRetried && error instanceof Neo4jError &&

@StephenCathcart StephenCathcart May 12, 2026

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.

might be misreading the flow here - _hasRetried is checked inside each wrapper, and _handleError walks the observer list calling them in turn. if someone attaches two consumers to the same result (result.keys() then await result or similar), does that mean the first wrapper sets _hasRetried = true and queues the retry, while the second wrapper sees the flag, falls into the else, releases the connection holder and rejects with the idempotent error?

do you think this is guarded elsewhere, or maybe just "bad usage" and ignore?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No this is indeed an issue, thank you! I think in my mental model, the fact that we unsubscribe from the stream observer would protect against this, but since the .forEach loop has started it will run the error callback from every observer that was subscribed when it started, even if the first one removes the subsequent entries!

I altered the code to instead of tracking if a result has retried, it tracks the exact error that it retried due to. As all the observers on the result will receive the exact same error object (not just an identical object but the same one) this can be used to safeguard against treating the same error as a 2nd error

@StephenCathcart StephenCathcart 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.

Other than open question looks good to me 👍

@MaxAake
MaxAake requested a review from StephenCathcart May 18, 2026 10:00

@StephenCathcart StephenCathcart 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.

👍

@MaxAake
MaxAake merged commit a98f9c5 into 6.x May 26, 2026
45 checks passed
@MaxAake
MaxAake deleted the idempotent-retries branch May 26, 2026 08:07
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