I’m trying to direct my read queries to my read replica in a production setup. Django allows defining multiple databases, so I added two databases like:
DATABASES = {
"default": {
"NAME": "ironbank",
"ENGINE": "django_postgrespool2",
"USER": "postgres",
"PASSWORD": os.getenv("DATABASE_PASSWORD", "postgres"),
"HOST": os.getenv("DATABASE_HOST", "localhost"),
"PORT": os.getenv("DATABASE_PORT", "5432"),
"CONN_MAX_AGE": 0,
},
"replica": {
"NAME": "ironbank_replica",
"ENGINE": "django_postgrespool2",
"USER": "postgres",
"PASSWORD": os.getenv("DATABASE_PASSWORD", "postgres"),
"HOST": os.getenv("REPLICA_DATABASE_HOST", "localhost"),
"PORT": os.getenv("REPLICA_DATABASE_PORT", "5432"),
"TEST": {
"MIRROR": "default",
},
"CONN_MAX_AGE": 0,
},
}
Here I’m using two different databases to test it setup locally.
I also have a database router configured as
file: settings.py
DATABASE_ROUTERS = ["ironbank.router.PrimaryReplicaRouter"]
---
file: ironbank/router/PrimaryReplicaRouter.py
class PrimaryReplicaRouter:
@staticmethod
def db_for_read(model, **hints):
"""
Reads go to a randomly-chosen replica.
"""
# some model specific logic here, but removed for simplicity
return "replica"
@staticmethod
def db_for_write(model, **hints):
"""
Writes always go to primary.
"""
return "default"
@staticmethod
def allow_relation(obj1, obj2, **hints):
"""
Relations between objects are allowed if both objects are
in the primary/replica pool.
"""
return True
@staticmethod
def allow_migrate(db, app_label, model_name=None, **hints):
"""
Allow migrations on default database only.
"""
return db == "default"
Findings
- I’ve been using shell_plus and manual data inspection to check the result being returned by the query is from master or replica. shell_plus directly shows the default database being used for all queries. Doing manual query has also verified data to be coming from master.
- I suspect django is not considering the
DATABASE_ROUTERS
configuration, as I filled in a random string, and got no error when doing a database query. - Even doing a syntax error in the class definition tells me django never actually read the class.
- Django version is 3.2, which supports the configuration.
- Using
.using("replica")
also did not work. As per docs, it overrides database router definition, and that doesn’t work either