diff --git a/doc/admin/config.rst b/doc/admin/config.rst index d45ff691f..7acd7cd1b 100644 --- a/doc/admin/config.rst +++ b/doc/admin/config.rst @@ -297,6 +297,12 @@ to speed up various operations:: [redis] location=redis://127.0.0.1:6379/1 sessions=false + sentinels=[ + ["sentinel_host_1", 26379], + ["sentinel_host_2", 26379], + ["sentinel_host_3", 26379] + ] + password=password ``location`` The location of redis, as a URL of the form ``redis://[:password]@localhost:6379/0`` @@ -305,6 +311,21 @@ to speed up various operations:: ``session`` When this is set to ``True``, redis will be used as the session storage. +``sentinels`` + Configures redis sentinels to use. + If you don't want to use redis sentinels, you should omit this option. + If this is set, redis via sentinels will be used instead of plain redis. + In this case the location should be of the form ``redis://my_master/0``. + The ``sentinels`` variable should be a json serialized list of sentinels, + each being a list with the two elements hostname and port. + You cannot provide a password within the location when using sentinels. + Note that the configuration format requires you to either place the entire + value on one line or make sure all values are indented by at least one space. + +``password`` + If your redis setup doesn't require a password or you already specified it in the location you can omit this option. + If this is set it will be passed to redis as the connection option PASSWORD. + If redis is not configured, pretix will store sessions and locks in the database. If memcached is configured, memcached will be used for caching instead of redis. @@ -343,11 +364,22 @@ an AMQP server (e.g. RabbitMQ) as a broker and redis or your database as a resul [celery] broker=amqp://guest:guest@localhost:5672// backend=redis://localhost/0 + broker_transport_options="{}" + backend_transport_options="{}" RabbitMQ might be the better choice if you have a complex, multi-server, high-performance setup, but as you already should have a redis instance ready for session and lock storage, we recommend redis for convenience. See the `Celery documentation`_ for more details. +The two ``transport_options`` entries can be omitted in most cases. +If they are present they need to be a valid JSON dictionary. +For possible entries in that dictionary see the `Celery documentation`_. + +To use redis with sentinels set the broker or backend to ``sentinel://sentinel_host_1:26379;sentinal_host_2:26379/0`` +and the respective transport_options to ``{"master_name":"mymaster"}``. +If your redis instances behind the sentinel have a password use ``sentinel://:my_password@sentinel_host_1:26379;sentinal_host_2:26379/0``. +If your redis sentinels themselves have a password set the transport_options to ``{"master_name":"mymaster","sentinel_kwargs":{"password":"my_password"}}``. + Sentry ------ diff --git a/src/pretix/settings.py b/src/pretix/settings.py index d5253271a..8784ab68d 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -38,6 +38,7 @@ import logging import os import sys from urllib.parse import urlparse +from json import loads import django.conf.locale from django.utils.crypto import get_random_string @@ -247,23 +248,35 @@ if HAS_MEMCACHED: } HAS_REDIS = config.has_option('redis', 'location') +USE_REDIS_SENTINEL = config.has_option('redis', 'sentinels') +HAS_REDIS_PASSWORD = config.has_option('redis', 'password') if HAS_REDIS: + OPTIONS = { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + "REDIS_CLIENT_KWARGS": {"health_check_interval": 30} + } + + if USE_REDIS_SENTINEL: + DJANGO_REDIS_CONNECTION_FACTORY = "django_redis.pool.SentinelConnectionFactory" + OPTIONS["CLIENT_CLASS"] = "django_redis.client.SentinelClient" + OPTIONS["CONNECTION_POOL_CLASS"] = "redis.sentinel.SentinelConnectionPool" + # See https://github.com/jazzband/django-redis/issues/540 + OPTIONS["SENTINEL_KWARGS"] = {"socket_timeout": 1} + OPTIONS["SENTINELS"] = [tuple(sentinel) for sentinel in loads(config.get('redis', 'sentinels'))] + + if HAS_REDIS_PASSWORD: + OPTIONS["PASSWORD"] = config.get('redis', 'password') + CACHES['redis'] = { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": config.get('redis', 'location'), - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - "REDIS_CLIENT_KWARGS": {"health_check_interval": 30} - } + "OPTIONS": OPTIONS } CACHES['redis_sessions'] = { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": config.get('redis', 'location'), "TIMEOUT": 3600 * 24 * 30, - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - "REDIS_CLIENT_KWARGS": {"health_check_interval": 30} - } + "OPTIONS": OPTIONS } if not HAS_MEMCACHED: CACHES['default'] = CACHES['redis'] @@ -279,9 +292,15 @@ if not SESSION_ENGINE: SESSION_ENGINE = "django.contrib.sessions.backends.db" HAS_CELERY = config.has_option('celery', 'broker') +HAS_CELERY_BROKER_TRANSPORT_OPTS = config.has_option('celery', 'broker_transport_options') +HAS_CELERY_BACKEND_TRANSPORT_OPTS = config.has_option('celery', 'backend_transport_options') if HAS_CELERY: CELERY_BROKER_URL = config.get('celery', 'broker') CELERY_RESULT_BACKEND = config.get('celery', 'backend') + if HAS_CELERY_BROKER_TRANSPORT_OPTS: + CELERY_BROKER_TRANSPORT_OPTIONS = loads(config.get('celery', 'broker_transport_options')) + if HAS_CELERY_BACKEND_TRANSPORT_OPTS: + CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS = loads(config.get('celery', 'backend_transport_options')) else: CELERY_TASK_ALWAYS_EAGER = True diff --git a/src/setup.py b/src/setup.py index 6f5d9c6f8..3a1524893 100644 --- a/src/setup.py +++ b/src/setup.py @@ -186,7 +186,7 @@ setup( 'django-oauth-toolkit==1.2.*', 'django-otp==0.7.*,>=0.7.5', 'django-phonenumber-field==4.0.*', - 'django-redis==4.11.*', + 'django-redis==5.0.*', 'django-scopes==1.2.*', 'django-statici18n==1.9.*', 'djangorestframework==3.12.*',