From f9646d9325df36e0c971f826d7f728bbf288faf1 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 8 Mar 2017 18:15:39 +0100 Subject: [PATCH] Do casual reads only on Galera --- src/pretix/base/views/async.py | 56 ++++++++++++++++------------------ src/pretix/helpers/database.py | 44 +++++--------------------- src/pretix/settings.py | 12 ++++++-- 3 files changed, 43 insertions(+), 69 deletions(-) diff --git a/src/pretix/base/views/async.py b/src/pretix/base/views/async.py index 1c0b1760fa..4b76db552b 100644 --- a/src/pretix/base/views/async.py +++ b/src/pretix/base/views/async.py @@ -9,7 +9,6 @@ from django.shortcuts import redirect, render from django.utils.translation import ugettext as _ from pretix.celery_app import app -from pretix.helpers.database import casual_reads logger = logging.getLogger('pretix.base.async') @@ -32,11 +31,10 @@ class AsyncAction: return JsonResponse(data) else: if res.ready(): - with casual_reads(): - if res.successful() and not isinstance(res.info, Exception): - return self.success(res.info) - else: - return self.error(res.info) + if res.successful() and not isinstance(res.info, Exception): + return self.success(res.info) + else: + return self.error(res.info) return redirect(self.get_check_url(res.id, False)) def get_success_url(self, value): @@ -66,25 +64,24 @@ class AsyncAction: 'ready': ready } if ready: - with casual_reads(): - if res.successful() and not isinstance(res.info, Exception): - smes = self.get_success_message(res.info) - if smes: - messages.success(self.request, smes) - # TODO: Do not store message if the ajax client states that it will not redirect - # but handle the mssage itself - data.update({ - 'redirect': self.get_success_url(res.info), - 'message': str(self.get_success_message(res.info)) - }) - else: - messages.error(self.request, self.get_error_message(res.info)) - # TODO: Do not store message if the ajax client states that it will not redirect - # but handle the mssage itself - data.update({ - 'redirect': self.get_error_url(), - 'message': str(self.get_error_message(res.info)) - }) + if res.successful() and not isinstance(res.info, Exception): + smes = self.get_success_message(res.info) + if smes: + messages.success(self.request, smes) + # TODO: Do not store message if the ajax client states that it will not redirect + # but handle the mssage itself + data.update({ + 'redirect': self.get_success_url(res.info), + 'message': str(self.get_success_message(res.info)) + }) + else: + messages.error(self.request, self.get_error_message(res.info)) + # TODO: Do not store message if the ajax client states that it will not redirect + # but handle the mssage itself + data.update({ + 'redirect': self.get_error_url(), + 'message': str(self.get_error_message(res.info)) + }) return data def get_result(self, request): @@ -93,11 +90,10 @@ class AsyncAction: return JsonResponse(self._return_ajax_result(res, timeout=0.25)) else: if res.ready(): - with casual_reads(): - if res.successful() and not isinstance(res.info, Exception): - return self.success(res.info) - else: - return self.error(res.info) + if res.successful() and not isinstance(res.info, Exception): + return self.success(res.info) + else: + return self.error(res.info) return render(request, 'pretixpresale/waiting.html') def success(self, value): diff --git a/src/pretix/helpers/database.py b/src/pretix/helpers/database.py index 910ea88079..e2d88419f9 100644 --- a/src/pretix/helpers/database.py +++ b/src/pretix/helpers/database.py @@ -1,7 +1,6 @@ import contextlib -from django.conf import settings -from django.db import connection, transaction +from django.db import transaction class DummyRollbackException(Exception): @@ -29,38 +28,9 @@ def rolledback_transaction(): raise Exception('Invalid state, should have rolled back.') -if 'mysql' in settings.DATABASES['default']['ENGINE'] and settings.DATABASE_IS_GALERA: - - @contextlib.contextmanager - def casual_reads(): - """ - When pretix runs with a MySQL galera cluster as a database backend, we can run into the - following problem: - - * A celery thread starts a transaction, creates an object and commits the transaction. - It then returns the object ID into celery's result store (e.g. redis) - - * A web thread pulls the object ID from the result store, but cannot access the object - yet as the transaction is not yet committed everywhere. - - This sets the wsrep_sync_wait variable to deal with this problem. - - See also: - - * https://mariadb.com/kb/en/mariadb/galera-cluster-system-variables/#wsrep_sync_wait - - * https://www.percona.com/doc/percona-xtradb-cluster/5.6/wsrep-system-index.html#wsrep_sync_wait - """ - with connection.cursor() as cursor: - cursor.execute("SET @wsrep_sync_wait_orig = @@wsrep_sync_wait;") - cursor.execute("SET SESSION wsrep_sync_wait = GREATEST(@wsrep_sync_wait_orig, 1);") - try: - yield - finally: - cursor.execute("SET SESSION wsrep_sync_wait = @wsrep_sync_wait_orig;") - -else: - - @contextlib.contextmanager - def casual_reads(): - yield +@contextlib.contextmanager +def casual_reads(): + """ + Kept for backwards compatibility. + """ + yield diff --git a/src/pretix/settings.py b/src/pretix/settings.py index 5afc9e6494..ee08724c8e 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -50,6 +50,14 @@ debug_fallback = "runserver" in sys.argv DEBUG = config.getboolean('django', 'debug', fallback=debug_fallback) db_backend = config.get('database', 'backend', fallback='sqlite3') +DATABASE_IS_GALERA = config.getboolean('database', 'galera', fallback=False) +if DATABASE_IS_GALERA and 'mysql' in db_backend: + db_options = { + 'init_command': 'SET SESSION wsrep_sync_wait = 1;' + } +else: + db_options = {} + DATABASES = { 'default': { 'ENGINE': 'django.db.backends.' + db_backend, @@ -58,10 +66,10 @@ DATABASES = { 'PASSWORD': config.get('database', 'password', fallback=''), 'HOST': config.get('database', 'host', fallback=''), 'PORT': config.get('database', 'port', fallback=''), - 'CONN_MAX_AGE': 0 if db_backend == 'sqlite3' else 120 + 'CONN_MAX_AGE': 0 if db_backend == 'sqlite3' else 120, + 'OPTIONS': db_options } } -DATABASE_IS_GALERA = config.getboolean('database', 'galera', fallback=False) STATIC_URL = config.get('urls', 'static', fallback='/static/')