-
Notifications
You must be signed in to change notification settings - Fork 2.9k
20878 use database routing when running script #20879
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,11 +2,13 @@ | |
| import traceback | ||
| from contextlib import ExitStack | ||
|
|
||
| from django.db import transaction | ||
| from django.db import router, transaction | ||
| from django.utils.translation import gettext as _ | ||
|
|
||
| from core.signals import clear_events | ||
| from dcim.models import Device | ||
| from extras.models import Script as ScriptModel | ||
| from netbox.context_managers import event_tracking | ||
| from netbox.jobs import JobRunner | ||
| from netbox.registry import registry | ||
| from utilities.exceptions import AbortScript, AbortTransaction | ||
|
|
@@ -42,10 +44,18 @@ def run_script(self, script, request, data, commit): | |
| # A script can modify multiple models so need to do an atomic lock on | ||
| # both the default database (for non ChangeLogged models) and potentially | ||
| # any other database (for ChangeLogged models) | ||
| with transaction.atomic(): | ||
| script.output = script.run(data, commit) | ||
| if not commit: | ||
| raise AbortTransaction() | ||
| branch_db = router.db_for_write(Device) | ||
| with transaction.atomic(using='default'): | ||
| # If branch database is different from default, wrap in a second atomic transaction | ||
| if branch_db != 'default': | ||
| with transaction.atomic(using=branch_db): | ||
| script.output = script.run(data, commit) | ||
| if not commit: | ||
| raise AbortTransaction() | ||
| else: | ||
| script.output = script.run(data, commit) | ||
| if not commit: | ||
| raise AbortTransaction() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to be clear, I don't think this guarantees true cross-connection atomicity, even though they are talking to the same DB, I'm pretty sure they are doing so via two separate connections. As it stands, outside of exceptional circumstances, I think that this is pretty safe and handles the the failure cases (where something failure in either the outer Do you think it would be worthwhile to add a comment here making it clear that (outside of the |
||
| except AbortTransaction: | ||
| script.log_info(message=_("Database changes have been reverted automatically.")) | ||
| if script.failed: | ||
|
|
@@ -108,14 +118,14 @@ def run(self, data, request=None, commit=True, **kwargs): | |
| script.request = request | ||
| self.logger.debug(f"Request ID: {request.id if request else None}") | ||
|
|
||
| # Execute the script. If commit is True, wrap it with the event_tracking context manager to ensure we process | ||
| # change logging, event rules, etc. | ||
| if commit: | ||
| self.logger.info("Executing script (commit enabled)") | ||
| with ExitStack() as stack: | ||
| for request_processor in registry['request_processors']: | ||
| stack.enter_context(request_processor(request)) | ||
| self.run_script(script, request, data, commit) | ||
| else: | ||
| self.logger.warning("Executing script (commit disabled)") | ||
|
|
||
| with ExitStack() as stack: | ||
| for request_processor in registry['request_processors']: | ||
| if not commit and request_processor is event_tracking: | ||
| continue | ||
| stack.enter_context(request_processor(request)) | ||
| self.run_script(script, request, data, commit) | ||
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.
Can we make this more generic, rather than talking specifically about a
branch_db?