Hi Erik,
On 02/10/2015 03:30 PM, Erik Cederstrand wrote:
> Den 10/02/2015 kl. 21.27 skrev Erik Cederstrand
> <
erik+...@cederstrand.dk>:
>>
>> Running this in parallel in two processes on the same machine
>> returns this after a while:
>>
>> Process A: [...]
>>
>> Process B: [...] Traceback (most recent call last): File "tmp.py",
>> line 30, in <module> assert cache.add(key='foo', value='bar')
>> AssertionError
>>
>>
>> I don't see how this is possible when I'm using
>> transaction.atomic().
>
> Phew. This situation is actually possible since "Read Committed" is
> the default isolation level in PostgreSQL, which means that
> non-repeatable reads are possible within a transaction.
Yes, that's what I said :-)
> My code relies on isolation level "Serializable" for a cache.get()
> followed by cache.add() to be reliable, but Django uses the default
> from PostgreSQL.
I don't think you need full Serializable, Repeatable Read should be
sufficient. Serializable is dangerous, has a tendency to cause deadlocks
(as I think you've discovered). Even with Repeatable Read, you'll need
be a bit careful about any long-running processes that might hold open a
transaction.
But `self.connection.set_isolation_level()` is called in
`_set_autocommit()`, and that should be sufficient. (The
`_set_isolation_level()` method is dead code in 1.7 and has since been
removed, but what really matters is that
`self.connection.set_isolation_level()` is called.)
OPTIONS['autocommit'] has been ignored since Django 1.6, as the docs you
linked clearly state: "This configuration is ignored and can be safely
removed."
You're looking for
https://docs.djangoproject.com/en/1.7/ref/settings/#autocommit instead.
(It would be good for the former to have a link to the latter, though.)
and
> hacking postgresql_psycopg2/base.py::_set_autocommit() to call
> _set_isolation_level(), but now process A blocks process B entirely,
> even when process A is outside the atomic() context manager.
>
> Has anyone got isolation_level "Serializable" to work in Django 1.7?
I strongly recommend against using the Serializable isolation level, or
turning off autocommit mode. The transaction.atomic() API will not work
correctly or predictably with autocommit turned off. See
https://docs.djangoproject.com/en/1.7/topics/db/transactions/#deactivate-transaction-management
You could switch to Repeatable Read (but leave AUTOCOMMIT on). Or (what
I would probably do) you could leave the isolation level at the default
(since it's the default for Django, some parts of Django, such as
QuerySet.get_or_create(), may not work correctly under a different
isolation level) and simply update your locking logic to guard against
that race condition (if the add fails, assume that another process has
grabbed the lock in the interim).
Carl