|select_for_update running on db_for_read||Ioan Alexandru Cucu||2/18/13 6:59 AM|
I wanted to raise a but around this, but I thought it might be a better idea to ask first on the developer's group.
If I'm running a select_for_update statement in a multidb environment that uses a read-only slave database, I get the following traceback:
File "/home/kux/workspace/src/other/django/django/core/handlers/base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "/home/kux/workspace/src/other/django/django/contrib/admin/options.py" in wrapper
366. return self.admin_site.admin_view(view)(*args, **kwargs)
File "/home/kux/workspace/src/other/django/django/utils/decorators.py" in _wrapped_view
91. response = view_func(request, *args, **kwargs)
File "/home/kux/workspace/src/other/django/django/views/decorators/cache.py" in _wrapped_view_func
89. response = view_func(request, *args, **kwargs)
File "/home/kux/workspace/src/other/django/django/contrib/admin/sites.py" in inner
196. return view(request, *args, **kwargs)
File "/home/kux/workspace/src/other/django/django/db/transaction.py" in inner
209. return func(*args, **kwargs)
File "/home/kux/workspace/src/other/django-cms/cms/admin/pageadmin.py" in wrap
File "/home/kux/workspace/src/other/django/django/db/models/query.py" in exists
562. return self.query.has_results(using=self.db)
File "/home/kux/workspace/src/other/django/django/db/models/sql/query.py" in has_results
441. return bool(compiler.execute_sql(SINGLE))
File "/home/kux/workspace/src/other/django/django/db/models/sql/compiler.py" in execute_sql
818. cursor.execute(sql, params)
File "/home/kux/workspace/src/other/django/django/db/backends/util.py" in execute
40. return self.cursor.execute(sql, params)
File "/home/kux/workspace/src/other/django/django/db/backends/mysql/base.py" in execute
114. return self.cursor.execute(query, args)
File "/home/kux/workspace/envs/hb23/local/lib/python2.7/site-packages/MySQLdb/cursors.py" in execute
174. self.errorhandler(self, exc, value)
File "/home/kux/workspace/envs/hb23/local/lib/python2.7/site-packages/MySQLdb/connections.py" in defaulterrorhandler
36. raise errorclass, errorvalue
Exception Type: DatabaseError at /admin/cms/page/add/
Exception Value: (1290, 'The MySQL server is running with the --read-only option so it cannot execute this statement')
Looking through the source code I found that django.db.models.query.QuerySet.select_for_update doesn't set the _for_write attribute before cloning the queryset.
Is this intended behaviour?
If not, the following patch would fix the issue:
@@ -663,6 +663,7 @@ class QuerySet(object):
# Default to false for nowait
nowait = kwargs.pop('nowait', False)
+ self._for_write = True
obj = self._clone()
obj.query.select_for_update = True
obj.query.select_for_update_nowait = nowait
Note: I'm running django 1.4.1
|Re: select_for_update running on db_for_read||Ioan Alexandru Cucu||2/18/13 7:42 AM|
The correct patch would be:
(Set _for_write on the cloned object rather than the original...)
@@ -664,6 +664,7 @@ class QuerySet(object):
obj = self._clone()
+ obj._for_write = True
|Re: select_for_update running on db_for_read||Shai Berger||2/19/13 11:23 AM|
Why does this operation make sense?
Why would you select-for-update if you don't intend to update? Or does
updating on a read-only slave somehow make sense?
|Re: select_for_update running on db_for_read||Ioan Alexandru Cucu||2/19/13 11:44 PM|
Well that's what I'm saying. It doesn't make any sense.
By default, django runs the select_for_update query on the 'for read' database instead of running it on the 'for write' database.
In order to make my code not to break, I need to explicitly tell django that I want the query to run on the 'for write' database.
=> I need to write:
pages = Page.objects.select_for_update().using(router.db_for_write(Page)).all()
instead of just
pages = Page.objects.select_for_update().all()
If I don't add the 'using(router.db_for_write(Page)' I get the traceback mentioned in my initial message.
|Re: select_for_update running on db_for_read||Russell Keith-Magee||2/19/13 11:58 PM|
I'd need to dig into the code to be 100% certain, but what you've described seems plausible, and the fix looks like it's going in the right direction. A ticket is certainly called for.
As for the patch -- it needs tests :-)
Django's test suite has support for testing multiple-database situations; check the regressiontests/multiple_database test suite for examples of how to use it. SELECT_FOR_UPDATE has it's own test suite - modeltests/select_for_update - so the tests probably belong in there.
Russ Magee %-)
|Re: select_for_update running on db_for_read||Ioan Alexandru Cucu||2/26/13 2:02 AM|