-
-
Notifications
You must be signed in to change notification settings - Fork 175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix exception in ActionCable channel due to reflexes overwriting each other's data #663
Fix exception in ActionCable channel due to reflexes overwriting each other's data #663
Conversation
✅ Deploy Preview for stimulusreflex ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Nice refactor ❤️ |
@Matt-Yorkley Thanks for your review. I have addressed your comments and also fixed the tests and linter warnings. Would appreciate if you could give it another look when you have time. |
@Matt-Yorkley Thanks for your suggestions! I have addressed all feedback. |
@Matt-Yorkley How can we proceed? Is there anything preventing this PR from getting merged? |
Hey @alexander-makarenko, thanks so much for working on this! Do you have an app or a test case which we could use to reproduce the issue itself? Just to make sure we are not re-introducing this by accident again in the future, even if it's a manual test. I just want to make sure we are not breaking other things with this change, as there are quite a few changes here. |
@marcoroth Since it's a race condition, it's actually quite difficult to reproduce, since it depends on how fast the StimulusReflex code runs in a specific application on a specific system. In a larger production application where I originally discovered the issue, it failed about once every 50 page loads. In the scenario that I outlined in the description of this PR (with no logic in the JS controllers or reflex), it seems to fail even rarer on my computer. In order to debug the issue, I remember that I cloned the repo of StimulusReflex 3.5.0.pre9 locally and pointed my application to use it. Then I added a sleep somewhere down Thus, due to the nature of the issue, I unfortunately won't be able to provide a test application or add an automated test for it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely great catch, I really think think this should be merged, the bug you uncovered is the kind that can haunt people for weeks if not months, we should prevent such pains!
I did not make a thorough review, but noticed one line removal, that may reintroduce an existing but. I don't think it's covered by a test. See my comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much for this @alexander-makarenko!
Bug fix + refactoring
Description
Think of a page with multiple StimulusReflex controllers hooked up to the
stimulus-reflex:connected
event and the markup (Haml in this example) looking something like this:The actions are backed by simple JavaScript controllers and reflex actions looking as follows. The reflex actions have different arity: one take no arguments, the other takes one.
This kind of setup creates a situation where
StimulusReflex::Channel#receive
is invoked twice in a very short period of time. And since both messages are processed by the same instance of the ActionCable channel (as can be verified by printing outself.object_id
at the beginning of the method), the use of an instance variable (@reflex_data
) in the current implementation leads to a situation where it gets overrwritten by the second message before the first one has been processed. As a result,reflex_data
inside#delegate_call_to_reflex
ends up containing the same set of data which, after being checked against the method signature of both of the reflex actions, raises anArgumentError
due to arity mismatch.This pull request fixes the issue by eliminating the use of instance variables inside
StimulusReflex::Channel#receive
in favour of bundling the message payload with the respective instance of the reflex. It also simplifies the reflex factory and constructor by delegating most of the calls to theStimulusReflex::ReflexData
instance.Why should this be added
In modern web applications, it is common to load parts of the page asynchronously for a more responsive user experience. With StimulusReflex, it is most easily achieved by hooking up to the
stimulus-reflex:connected
event. This pull request fixes an exception that prevented the use of the specified event with multiple reflexes.Checklist