私の現在の選択は、PostgreSQL Advisory Locksを使用することです。そして、それはうまくいくようです。
以下は、データベースで動作する他のすべてのミドルウェアの上に配置されるミドルウェア クラスです。大量の接続用に構成された別の postgres クラスターと、セッション モードでの別の接続プーラーを使用することをお勧めします。
from django.contrib.auth import SESSION_KEY as USER_ID_SESSION_KEY
class SessionLock(object):
"""
Prevents users from making simultaneous requests.
"""
def __init__(self):
if not getattr(settings, 'SESSION_LOCK', True):
raise MiddlewareNotUsed
from django.db import connections, DEFAULT_DB_ALIAS
from django.contrib.sessions.middleware import SessionMiddleware
self.connections = connections
self.db_alias = getattr(settings, 'SESSION_LOCK_DB', DEFAULT_DB_ALIAS)
# Get session initialisation function from session middleware.
self.load_session = SessionMiddleware.process_request.__func__
def process_request(self, request):
# Load the session to retrieve user id from it.
self.load_session(None, request)
# Generate a lock id.
user_id = request.session.get(USER_ID_SESSION_KEY)
if user_id is not None:
request.session_lock_id = ('user_lock_%d' % user_id).__hash__()
else:
# If user is anonymous then use meta info for identification.
request.session_lock_id = (
request.META.get('HTTP_HOST', '') + ':' +
request.META.get('HTTP_USER_AGENT', '')
).__hash__()
# Acquire the lock.
cursor = self.connections[self.db_alias].cursor()
cursor.execute('SELECT pg_advisory_lock(%d)' % request.session_lock_id)
def process_response(self, request, response):
self._release_lock(request)
return response
def process_exception(self, request, exception):
self._release_lock(request)
def _release_lock(self, request):
if hasattr(request, 'session_lock_id'):
cursor = self.connections[self.db_alias].cursor()
cursor.execute('SELECT pg_advisory_unlock(%d)' %
request.session_lock_id)