Django - Middleware теряет соединение с базой данных в многопользовательском приложении
В моем приложении Django я использую многопользовательский подход с изолированными базами данных. Он работает хорошо, но поскольку он опирается на субдомены для каждого арендатора, что не является масштабируемым, я пытаюсь изменить это. Для достижения этой функциональности я пытаюсь использовать промежуточные модули и сессии для получения арендатора из имени пользователя и использования его для установки локальной переменной для маршрутизаторов базы данных. Логика такова:
Если пользователь не вошел в систему, BeforeLoginMiddleware активируется и получает от пользователя имя арендатора. Таким образом, username@tenant1 установит tenant1 в сессию. Вот код:
import threading
from users.forms import LoginForm
Thread_Local = threading.local()
class BeforeLoginMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.path == '/login/':
form = LoginForm(request.POST)
if form.is_valid():
complete_username = form.cleaned_data.get('username')
current_db = complete_username.split('@')[1]
request.session['current_db'] = current_db
request.session.modified = True
response = self.get_response(request)
return response
Если пользователь уже вошел в систему, второе промежуточное ПО получит данные арендатора из сессии и использует их для определения переменной Thread_Local, которая вызывается в функции, используемой на маршрутизаторах базы данных:
class AppMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
current_db = request.session.get('current_db')
setattr(Thread_Local, 'current_db', current_db)
response = self.get_response(request)
return response
def get_current_db_name():
return getattr(Thread_Local, 'current_db', None)
Вот файл routers.py:
class AppRouter:
def db_for_read(self, model, **hints):
return get_current_db_name()
def db_for_write(self, model, **hints):
return get_current_db_name()
def allow_relation(self, *args, **kwargs):
return True
def allow_syncdb(self, *args, **kwargs):
return None
def allow_migrate(self, *args, **kwargs):
return None
Вот настройки промежуточного ПО в моем приложении:
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'myapp.middleware.BeforeLoginMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'myapp.middleware.AppMiddleware',
]
Это работает, как и ожидалось, но в случайных случаях (или, по крайней мере, кажется, что в случайных) он теряет связь с базой данных и, как я предполагаю, поскольку он не может получить данные сессии, снова перенаправляет пользователя на страницу входа (я также использую декоратор login_required). Иногда я могу переходить на несколько разных страниц, и все в порядке, а затем происходит отключение. В других случаях он отключается при переходе на первую страницу после главной, после входа в систему. Это также происходит, если страница простаивает около 1 минуты, что не имеет смысла, потому что мой параметр SESSION_COOKIE_AGE равен 1200.
Дело в том, что я понятия не имею, что вызывает это, потому что нет никаких ошибок. Единственное, что я заметил, когда это происходит, это статус 302, зарегистрированный на вкладке "Сеть" в браузере (который является перенаправлением на страницу входа), и сообщение "Сломанная труба от ('127.0.0.1', 61980) в терминале с этим случайным кодом в конце.
Что я пробовал до сих пор, без каких-либо изменений в поведении, описанном выше:
- Множество различных настроек для промежуточного ПО. Например: сначала я написал одно промежуточное ПО со всей логикой и поместил его в конец списка промежуточных программ. Я подумал, что это может быть причиной проблемы, поэтому я разделил логику. Другая попытка заключалась в использовании request.user.is_authenticated в промежуточном ПО вместо использования данных сессии.
- Основываясь на этом ответе, я попытался отключить все JS скрипты в приложения (их всего несколько). Это не совсем выход, но я думаю, что это исключает возможность быть причиной.
- Полностью очистите кэш/куки браузера.
- Измените настройку SESSION_SAVE_EVERY_REQUEST на True.
- Измените значение параметра SESSION_COOKIE_AGE на 120000 (я думаю, что увеличивать это число не имеет смысла, но на данный момент я готов попробовать все).
Честно говоря, у меня уже нет вариантов, поэтому я благодарен за любую помощь или совет.