[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
Schedule
model:Although modifying the router works, I would suggest adding a
has_replica
boolean attribute in the Django Q settings.In the
conf.py
, add aHAS_REPLICA
attribute.And the
scheduler
function in thecluster.py
will 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_update
will always be made using the write database, which must be configured in theorm
setting). 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_replica
in the Django Q settings?If it does can I open a PR suggesting these additions and tests covering some scenarios around the multiple databases?