Skip to content

The delete_expired method of ResultManager sometimes selects read-only database #357

Open
@jacklinke

Description

@jacklinke

In a django project with a database router and read-only databases, the delete_expired method may try to perform raw_delete() using a read-only database, resulting in an Internal Error: "ReadOnlySqlTransaction - cannot execute DELETE in a read-only transaction".

Relevant code:

def delete_expired(self, expires):
    """Delete all expired results."""
    with transaction.atomic(using=self.db):
        raw_delete(queryset=self.get_all_expired(expires))

My Database Router:

import random

from django.conf import settings


class CustomRouter:
    def db_for_read(self, model, **hints):
        """
        Return either default or one of the replicas
        """

        db_read = [
            "default",
        ]

        if settings.REPLICA1_URL is not None:
            db_read.append("replica1")
        if settings.REPLICA2_URL is not None:
            db_read.append("replica2")

        return random.choice(db_read)

    def db_for_write(self, model, **hints):
        # Always return the default database
        return "default"

    def allow_relation(self, obj1, obj2, **hints):
        return True

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        return True

Occasionally, django-celery-results will attempt to conduct delete_expired using one of my replica databases:

connection: <DatabaseWrapper vendor='postgresql' alias='replica1'>

Recommend changing the using argument of the atomic transaction in delete_expired to explicitly use the writable database specified in the router.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions