From 125e867ffff9983be155fbd83d490d65b03db331 Mon Sep 17 00:00:00 2001 From: Bram Date: Sun, 12 Apr 2026 17:10:02 +0200 Subject: [PATCH] Add database connection health check (#5170) --- backend/src/baserow/config/celery.py | 22 +++++++++++++++++++ backend/src/baserow/config/settings/base.py | 6 +++++ .../add_database_connection_health_check.json | 9 ++++++++ 3 files changed, 37 insertions(+) create mode 100644 changelog/entries/unreleased/bug/add_database_connection_health_check.json diff --git a/backend/src/baserow/config/celery.py b/backend/src/baserow/config/celery.py index eb95d46b0d..7621e93af2 100644 --- a/backend/src/baserow/config/celery.py +++ b/backend/src/baserow/config/celery.py @@ -27,8 +27,30 @@ def clear_local(*args, **kwargs): clear_db_state() +def close_old_db_connections(sender, **kwargs): + """ + Close old or unusable database connections around each Celery task. + + Calling `close_old_connections()` on both `task_prerun` and `task_postrun` + ensures Django's own connection lifecycle is applied to Celery tasks, matching the + documented recommendation for long-running processes. + + Eager tasks (used in tests) are skipped because they run inside the + caller's process and share its database connection/transaction. + """ + + if getattr(sender.request, "is_eager", False): + return + + from django.db import close_old_connections + + close_old_connections() + + signals.task_prerun.connect(clear_local) +signals.task_prerun.connect(close_old_db_connections) signals.task_postrun.connect(clear_local) +signals.task_postrun.connect(close_old_db_connections) @signals.worker_process_init.connect diff --git a/backend/src/baserow/config/settings/base.py b/backend/src/baserow/config/settings/base.py index 54072fb3ac..e49e089bf8 100644 --- a/backend/src/baserow/config/settings/base.py +++ b/backend/src/baserow/config/settings/base.py @@ -283,6 +283,12 @@ DATABASE_READ_REPLICAS.append(db_key) +# Enable connection health checks for all database connections. This makes Django +# verify that a database connection is still usable before each request/task, which +# prevents "connection already closed" errors when connections are dropped by the +# server, a load balancer, or a connection pooler. +for _db_key in DATABASES: + DATABASES[_db_key].setdefault("CONN_HEALTH_CHECKS", True) DATABASE_ROUTERS = ["baserow.config.db_routers.ReadReplicaRouter"] diff --git a/changelog/entries/unreleased/bug/add_database_connection_health_check.json b/changelog/entries/unreleased/bug/add_database_connection_health_check.json new file mode 100644 index 0000000000..68a6897cab --- /dev/null +++ b/changelog/entries/unreleased/bug/add_database_connection_health_check.json @@ -0,0 +1,9 @@ +{ + "type": "bug", + "message": "Add database connection health check.", + "issue_origin": "github", + "issue_number": null, + "domain": "core", + "bullet_points": [], + "created_at": "2026-04-12" +}