-
Notifications
You must be signed in to change notification settings - Fork 4
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 transaction issues #36
Conversation
When running inside an existing transaction the disable_side_effects decorator has a bug. It disables and then enables the registry so that side-effects don't run, but if the call to `run_side_effects` is deferred using `transaction.on_commit` it may be called after the context manager has exited, in which case the registry will have been re-enabled. In this case the side-effects will run. The fix is to pull some of the logic around suppression out so that it runs immediately on calling `run_side_effects` - if the registry is disabled at that point it will fire the signal and return immediately.
# when in TEST_MODE do not defer the side-effects, as they | ||
# are never fired anyway. | ||
registry_func = ( | ||
registry.run_side_effects | ||
if TEST_MODE | ||
else registry.run_side_effects_on_commit | ||
) | ||
registry_func(label, *args, return_value=return_value, **kwargs) | ||
registry.run_side_effects(label, *args, return_value=return_value, **kwargs) |
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.
Part one of the change is to always call run_side_effects
rather than adding in fancy logic to the decorator. Simpler.
side_effects/registry.py
Outdated
# if the registry is suppressed we are inside disable_side_effects, | ||
# so we send the signal and return early. | ||
if _registry.is_suppressed: | ||
suppressed_side_effect.send(Registry, label=label) | ||
else: | ||
transaction.on_commit( | ||
partial( | ||
_registry.run_side_effects, | ||
label, | ||
*args, | ||
return_value=return_value, | ||
**kwargs, | ||
) |
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.
This is the meat of the change. Instead of having two separate methods we now always run the method on_commit, but we abort early if _registry.is_suppressed
- which is True inside the disable_side_effects
context. This means that we immediately return regardless of the transaction. The transaction is only relevant when actually running the side-effects for real, which is the desired behaviour.
1e0d552
to
0bd8ae4
Compare
# TODO: this is all becoming over-complex - need to simplify this | ||
self.try_bind_all(label, *args, return_value=return_value, **kwargs) | ||
if self.is_suppressed: | ||
self.suppressed_side_effect.send(Registry, label=label) | ||
else: | ||
self._run_side_effects(label, *args, return_value=return_value, **kwargs) |
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.
TODONE - it was too complex so I have removed it.
864a6aa
to
8f21d92
Compare
This is a refactoring of the internals so that when using the
disable_side_effects
context manager or decorator therun_side_effects
method call is completely bypassed, and thesuppressed_side_effect
signal is immediately fired, rather than deferred tilltransaction.on_commit
.This should fix a bug whereby side-effects were deferred using
transaction.on_commit
, which meant that when they were called the registry was outside of the context manager and theis_suppressed
attr was no longer true. i.e. side-effects were fired even when usingdisable_side_effects
correctly.