#26332: `BaseCache.get_or_set()` violates principle of least astonishment
-------------------------------------+--------------------
Reporter: suligap | Owner: nobody
Type: Uncategorized | Status: new
Component: Core (Cache system) | Version: 1.9
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 1
Easy pickings: 0 | UI/UX: 0
-------------------------------------+--------------------
[https://github.com/django/django/blob/2109975e901440da70e29d0f330a600bc2d37e9a/django/core/cache/backends/base.py#L150-L169
BaseCache.get_or_set()] returns `False` if the key was set in cache
between the first `.get()` and `.add()`.
The reason for using `.add()` instead of `.set()` in there is a "big race
condition" as stated in [https://code.djangoproject.com/ticket/12982
12982]. However, it is very unlikely that the caller will pass a `default`
callable that reads the key from the cache and returns a result that
depends on the value currently stored in cache which would be a
prerequisite for a race condition to occur. Using `set()` as originally
stated in the description of [https://code.djangoproject.com/ticket/12982
12982] means that there will be a ''data race'' which doesn't affect the
correctness of the program, because the result is that multiple writes
happened, but all of them are correct.
Following texts contain a nice explanation of what is a race condition and
how it relates to a data race:
- [http://blog.regehr.org/archives/490 Race Condition vs. Data Race]
- [https://support.microsoft.com/en-us/kb/317723 Description of race
conditions and deadlocks]
As a user of `get_or_set()` I am expecting it to always return a value,
regardless if a write to the cache happened and it is inefficient for me
to handle the `False` return scenario by calling my `default()` callback
again to obtain the value I need (it was already executed inside
.get_or_set, but the result was discarded).
I can see 2 possible fixes:
- always use `.set()` and always return the value of default
- ignore the result of `.add()` and always return to the user with the
result of `.get(key, default)` after the call to `.add()`
--
Ticket URL: <https://code.djangoproject.com/ticket/26332>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/050.a72a39bd2f42efb0c627ba4d82a4cbf6%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.