Clone database connection on manager.execute

This commit is contained in:
Marc Aymerich 2015-05-09 11:41:52 +00:00
parent c5140ccbae
commit 6fa29a15b8
7 changed files with 46 additions and 12 deletions

View File

@ -355,3 +355,5 @@ make django admin taskstate uncollapse fucking traceback, ( if exists ?)
# backend.context and backned.instance provided when an action is called? like forms.cleaned_data: do it on manager.generation(backend.context = backend.get_context()) or in backend.__getattr__ ? also backend.head,tail,content switching on manager.generate()?
# replace return_code by exit_code everywhere
# plan.rate registry

View File

@ -167,7 +167,11 @@ class ServiceBackend(plugins.Plugin, metaclass=ServiceMount):
run = bool(self.scripts) or (self.force_empty_action_execution or bool(self.content))
if not run:
state = BackendLog.NOTHING
log = BackendLog.objects.create(backend=self.get_name(), state=state, server=server)
using = kwargs.pop('using', None)
manager = BackendLog.objects
if using:
manager = manager.using(using)
log = manager.create(backend=self.get_name(), state=state, server=server)
return log
def execute(self, server, async=False, log=None):

View File

@ -6,7 +6,7 @@ from functools import partial
from django.core.mail import mail_admins
from orchestra.utils.db import close_connection
from orchestra.utils import db
from orchestra.utils.python import import_class, OrderedSet
from . import settings, Operation
@ -31,7 +31,7 @@ def keep_log(execute, log, operations):
send_report(execute, args, log)
except Exception as e:
trace = traceback.format_exc()
log.state = BackendLog.EXCEPTION
log.state = log.EXCEPTION
log.stderr = trace
log.save()
subject = 'EXCEPTION executing backend(s) %s %s' % (str(args), str(kwargs))
@ -122,12 +122,9 @@ def execute(scripts, serialize=False, async=None):
kwargs = {
'async': async,
}
log = backend.create_log(*args, **kwargs)
# TODO Perform this shit outside of the current transaction in a non-hacky way
#t = threading.Thread(target=backend.create_log, args=args, kwargs=kwargs)
#t.start()
#log = t.join()
# End of hack
with db.clone(model=BackendLog) as handle:
log = backend.create_log(*args, using=handle.target)
log._state.db = handle.origin
kwargs['log'] = log
task = keep_log(backend.execute, log, operations)
logger.debug('%s is going to be executed on %s.' % (backend, route.host))
@ -135,7 +132,7 @@ def execute(scripts, serialize=False, async=None):
# Execute one backend at a time, no need for threads
task(*args, **kwargs)
else:
task = close_connection(task)
task = db.close_connection(task)
thread = threading.Thread(target=task, args=args, kwargs=kwargs)
thread.start()
if not async:

View File

@ -28,6 +28,7 @@ def grant_permission(modeladmin, request, queryset):
for user in queryset:
user.grant_to = to
user.grant_ro = ro
# DOn't collect, execute right away for path validation
OperationsMiddleware.collect('grant_permission', instance=user)
context = {
'type': _("read-only") if ro else _("read-write"),
@ -35,6 +36,7 @@ def grant_permission(modeladmin, request, queryset):
}
msg = _("Granted %(type)s permissions on %(to)s") % context
modeladmin.log_change(request, user, msg)
# TODO feedback message
return
opts = modeladmin.model._meta
app_label = opts.app_label

View File

@ -72,7 +72,7 @@ class UNIXUserBackend(ServiceController):
'to': user.grant_to,
'ro': user.grant_ro,
})
if user.ro:
if user.grant_ro:
self.append('echo "acl add read permissions for %(user)s to %(to)s"' % context)
else:
self.append('echo "acl add read-write permissions for %(user)s to %(to)s"' % context)

View File

@ -37,7 +37,7 @@
<div class="field-box field-home">
{{ form.path_extension.errors }}
<label for="{{ form.base_path.id_for_label }}">{{ form.base_path.label }}:</label>
{{ form.base_path }}
{{ form.base_path }}{% for x in ""|ljust:"50" %}&nbsp;{% endfor %}
<p class="help">{{ form.base_path.help_text|safe }}</p>
</div>
<div class="field-box field-home">

View File

@ -1,6 +1,7 @@
import sys
from django import db
from django.conf import settings as djsettings
def running_syncdb():
@ -31,3 +32,31 @@ def close_connection(execute):
finally:
db.connection.close()
return wrapper
class clone(object):
"""
clone database in order to have fresh connections and make queries outside the current transaction
with db.clone(model=BackendLog) as handle:
log = BackendLog.objects.using(handle.target).create()
log._state.db = handle.origin
"""
def __init__(self, model=None, origin='', target=''):
if model is not None:
origin = db.router.db_for_write(model)
self.origin = origin or db.DEFAULT_DB_ALIAS
self.target = target or 'other_' + origin
def __enter__(self):
djsettings.DATABASES[self.target] = djsettings.DATABASES[self.origin]
# Because db.connections.datases is a cached property
self.old_connections = db.connections
db.connections = db.utils.ConnectionHandler()
return self
def __exit__(self, type, value, traceback):
db.connections[self.target].close()
djsettings.DATABASES.pop(self.target)
db.connections = self.old_connections