[Error] select_for_update happening when using replica(read-only) and default(write-only) DB.
See original GitHub issueI’ve been getting a select_for_update cannot be used outside of a transaction error when using a replica set in my applications.
Here are my settings
DATABASES = {
"default": {
"ENGINE": os.getenv("DB_ENGINE"),
"NAME": os.getenv("DB_NAME"),
"USER": os.environ.get("DB_USER"),
"HOST": os.environ.get("DB_HOST"),
"PORT": os.environ.get("DB_PORT"),
"PASSWORD": os.environ.get("DB_PASSWORD"),
},
"replica": {
"ENGINE": os.getenv("DB_ENGINE"),
"NAME": os.getenv("DB_NAME_REPLICA"),
"USER": os.environ.get("DB_USER_REPLICA"),
"HOST": os.environ.get("DB_HOST_REPLICA"),
"PORT": os.environ.get("DB_PORT_REPLICA"),
"PASSWORD": os.environ.get("DB_PASSWORD_REPLICA"),
}
}
Q_CLUSTER = {
"name": "myscheduler",
"orm": "default", # Use Django's ORM + database for broker
....
}
My database router currently uses the replica only to read and the default just to write.
class DatabaseRouter:
def db_for_read(self, model, **hints):
"""Always read from REPLICA database"""
return "replica"
def db_for_write(self, model, **hints):
"""Always write to DEFAULT database"""
return "default"
def allow_relation(self, obj1, obj2, **hints):
"""Objects from REPLICA and DEFAULT are de same, then True always"""
return True
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""Only DEFAULT database"""
return db == "default"
I’ve been digging through the code (awesome documenation btw!) and found that during the task creation in the scheduler function it forces the database used in the transaction block.
# Here it seems to force the usage, in this case it will be the replica database.
with db.transaction.atomic(using=Schedule.objects.db):
for s in (
Schedule.objects.select_for_update()
.exclude(repeats=0)
.filter(next_run__lt=timezone.now())
.filter(db.models.Q(cluster__isnull=True) | db.models.Q(cluster=Conf.PREFIX))
):
Is there a reason for this behaviour? I couldn’t really understand why, since when removing the using from the transaction block made it work like a charm, reading only from replica and writing only on default.
Dependencies
- python = 3.9.5
- Django = 3.1.7
- psycopg2-binary = "2.8.6
- django-q = 1.3.6
Issue Analytics
- State:
- Created 2 years ago
- Reactions:5
- Comments:9 (7 by maintainers)
Top Results From Across the Web
Database cannot be upgraded because it is read-only or has ...
When I try to attach it, it throws an error message with "attach database failed". Closing SSMS and reopening it as an Administrator...
Read more >Strange MySQL "read-only" error - database - Stack Overflow
If you're in AWS Aurora, you might be accessing the replica instance which is read-only so you need to use the DB Cluster...
Read more >How to change Postgresql database from Read-only to Writable
Since SELECT pg_is_in_recovery() is true you're connected to a read-only replica server in hot_standby mode. The replica configuration is in ...
Read more >MySQL Replication
more MySQL database servers (known as replicas). Replication is asynchronous by default; replicas do not need to be connected permanently to receive updates ......
Read more >SQL SERVER - Error: Fix for Error Msg 3906 - Failed to update ...
Let us mark the just created database as ReadOnly. USE MASTER GO ALTER DATABASE [ReaOnlyDB] SET READ_ONLY GO.
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

We are also experiencing this issue. As a temporary workaround, we modified our database router to always use the write db for Django-Q’s
Schedulemodel:Although modifying the router works, I would suggest adding a
has_replicaboolean attribute in the Django Q settings.In the
conf.py, add aHAS_REPLICAattribute.And the
schedulerfunction in thecluster.pywill now look like this:When we use read/write replicas we don’t specify a database to use in the transaction. The transaction will be made without any problems, since the router will correctly use the write database when a write operation is made (in this case the
select_for_updatewill always be made using the write database, which must be configured in theormsetting). All the other read operations will be made using the read database, just as intended.The scenarios where some apps needs to write into one database and other apps into another, like it was suggested in a previous modification, would also works correctly when applying the
with db.transaction.atomic(using=Schedule.objects.db)That way the current usage would not break and it would allow the usage of read only replicas via a setting in the configuration.
What do you all think? Does it make sense to add the
has_replicain the Django Q settings?If it does can I open a PR suggesting these additions and tests covering some scenarios around the multiple databases?