Skip to content
Snippets Groups Projects
Verified Commit b250fc12 authored by Nik | Klampfradler's avatar Nik | Klampfradler
Browse files

Use separate database connection for Celery results

This ensures Celery progress is always updated and queried outside a
transaction.
parent dab32732
No related branches found
No related tags found
1 merge request!943Use separate database connection for Celery results
Pipeline #52550 failed
...@@ -15,6 +15,12 @@ Changed ...@@ -15,6 +15,12 @@ Changed
* [Dev] The (undocumented) setting PDF_CONTEXT_PROCESSORS is now named NON_REQUEST_CONTEXT_PROCESSORS * [Dev] The (undocumented) setting PDF_CONTEXT_PROCESSORS is now named NON_REQUEST_CONTEXT_PROCESSORS
* [Docker] Cache is now cleared if migrations are applied * [Docker] Cache is now cleared if migrations are applied
Fixed
~~~~~
* Celery progress could be inaccurate if recording progress during a transaction
`2.7.1`_ - 2022-01-28 `2.7.1`_ - 2022-01-28
--------------------- ---------------------
......
...@@ -227,6 +227,13 @@ DATABASES = { ...@@ -227,6 +227,13 @@ DATABASES = {
} }
} }
# Duplicate default database for out-of-transaction updates
DATABASES["default_oot"] = DATABASES["default"].copy()
DATABASE_ROUTERS = [
"aleksis.core.util.core_helpers.OOTRouter",
]
DATABASE_OOT_LABELS = ["django_celery_results"]
merge_app_settings("DATABASES", DATABASES, False) merge_app_settings("DATABASES", DATABASES, False)
REDIS_HOST = _settings.get("redis.host", "localhost") REDIS_HOST = _settings.get("redis.host", "localhost")
......
...@@ -390,3 +390,54 @@ def create_default_celery_schedule(): ...@@ -390,3 +390,54 @@ def create_default_celery_schedule():
PeriodicTask.objects.create( PeriodicTask.objects.create(
name=f"{name} (default schedule)", task=name, **{attr: db_schedule} name=f"{name} (default schedule)", task=name, **{attr: db_schedule}
) )
class OOTRouter:
"""Database router for operations that should run out of transaction.
This router routes database operations for certain apps through
the separate default_oot connection, to ensure that data get
updated immediately even during atomic transactions.
"""
default_db = "default"
oot_db = "default_oot"
@property
def oot_labels(self):
return settings.DATABASE_OOT_LABELS
@property
def default_dbs(self):
return set((self.default_db, self.oot_db))
def is_same_db(self, db1: str, db2: str):
return set((db1, db2)).issubset(self.default_dbs)
def db_for_read(self, model: Model, **hints) -> Optional[str]:
if model._meta.app_label in self.oot_labels:
return self.oot_db
return None
def db_for_write(self, model: Model, **hints) -> Optional[str]:
return self.db_for_read(model, **hints)
def allow_relation(self, obj1: Model, obj2: Model, **hints) -> Optional[bool]:
# Allow relations between default database and OOT connection
# They are the same database
if self.is_same_db(obj1._state.db, obj2._state.db):
return True
return None
def allow_migrate(
self, db: str, app_label: str, model_name: Optional[str] = None, **hints
) -> Optional[bool]:
# Never allow any migrations on the default_oot database
# It connects to the same database as default, so everything
# migrated there
if db == self.oot_db:
return False
return None
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment