Caching at model class level

24 views
Skip to first unread message

Thomas Guettler

unread,
Nov 3, 2011, 10:22:51 AM11/3/11
to Django users
Hi,

I try to reduce the number of db-queries in my app.

There is a model which changes almost never. It is like a "type of ticket"
in a trouble ticket system.

On one page there are seven SQL-Queries (SELECT .... FROM ticket_type where id=123) which of course always return
the same result.

I want to cache the objects:

t1=TicketType.objects.get(id=123)
t2=TicketType.objects.get(id=123)

t1 and t2 should be the identical python object, not just objects containing the same data.

Has someone done this before?

Here is my first version:

class ThreadLocalQueryset(models.query.QuerySet):
_threadlocal=threading.local()
_threadlocal.cache=dict() # The cache is at class-level. It survives a request.
def get(self, **kwargs):
kwargs_tuple=tuple(kwargs.items())
obj=self._threadlocal.cache.get(kwargs_tuple)
if obj is not None:
return obj
obj=models.query.QuerySet.get(self, **kwargs)
self._threadlocal.cache[kwargs_tuple]=obj
return obj

class ThreadLocalManager(models.Manager):
use_for_related_fields = True
def get_query_set(self):
return ThreadLocalQueryset(self.model, using=self._db)

class TicketType(models.Model):
objects=ThreadLocalManager()

If there would be many TicketTypes, the interpreter would use more and more memory, but there are few.

Feedback welcome,
Thomas Güttler

--
Thomas Guettler, http://www.thomas-guettler.de/
E-Mail: guettli (*) thomas-guettler + de

Donald Stufft

unread,
Nov 3, 2011, 10:29:44 AM11/3/11
to django...@googlegroups.com
Normally this is cached using the Django caching framework, this lets you save it in memcache, redis, etc
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

Thomas Guettler

unread,
Nov 3, 2011, 11:33:47 AM11/3/11
to django...@googlegroups.com
Am 03.11.2011 15:29, schrieb Donald Stufft:
> Normally this is cached using the Django caching framework, this lets
> you save it in memcache, redis, etc

The django caching frameworks provides decorators for caching complete
web pages. And it provides an API for caching strings or objects which can get pickled.

It does not provide caching at object level, if new query sets get used (like related
object look-up).

Try it yourself. This middleware shows how many times the same SQL query was executed:

http://djangosnippets.org/snippets/344/


Thomas

> On Thursday, November 3, 2011 at 10:22 AM, Thomas Guettler wrote:
>
>> Hi,
>>
>> I try to reduce the number of db-queries in my app.
>>
>> There is a model which changes almost never. It is like a "type of ticket"
>> in a trouble ticket system.
>>
>> On one page there are seven SQL-Queries (SELECT .... FROM ticket_type
>> where id=123) which of course always return
>> the same result.
>>

Andre Terra

unread,
Nov 3, 2011, 12:25:52 PM11/3/11
to django...@googlegroups.com
Django caching frameworks also provide a low level API[1].

sebleier's django-redis-cache is one great tool for the job [2]. In order to use it, you will of course need redis too [3].


Cheers,
AT

[1] https://docs.djangoproject.com/en/dev/topics/cache/#the-low-level-cache-api
[2] https://github.com/sebleier/django-redis-cache
[3] http://redis.io



Tom Evans

unread,
Nov 3, 2011, 1:42:22 PM11/3/11
to django...@googlegroups.com

Hey

I wouldn't use this approach - caching in threadlocals is the wrong
place to cache. Primarily, this is because the cache is in the wrong
location - it is per thread, which means you must have n caches for n
threads. This is an inefficient use of memory, it would be better to
cache at a different level, eg memcached, redis, etc.

Secondly, this caching is very naïve. Queries are never evicted from
the cache, and so any updates to the underlying table will not be
reflected in versions that are cached. Different threads could then
cache different results for the same query, and you will end up with
heisenbugs - where the behaviour changes the closer you examine it.

You can fix all these issues by using a real cache engine like
memcached, set appropriate timeouts etc. Items retrieved from the
cache will be consistent, even amongst different threads. You can use
the query from the queryset as the key.

Cheers

Tom

Thomas Guettler

unread,
Nov 4, 2011, 3:02:01 AM11/4/11
to django...@googlegroups.com
Am 03.11.2011 18:42, schrieb Tom Evans:
> On Thu, Nov 3, 2011 at 2:22 PM, Thomas Guettler <h...@tbz-pariv.de> wrote:
>> Hi,
>>
>> I try to reduce the number of db-queries in my app.
>>
>> There is a model which changes almost never. It is like a "type of ticket"
>> in a trouble ticket system.
>>
>> On one page there are seven SQL-Queries (SELECT .... FROM ticket_type where id=123) which of course always return
>> the same result.
>>
>> I want to cache the objects:
>>
>> t1=TicketType.objects.get(id=123)
>> t2=TicketType.objects.get(id=123)

> Hey


>
> I wouldn't use this approach - caching in threadlocals is the wrong
> place to cache. Primarily, this is because the cache is in the wrong
> location - it is per thread, which means you must have n caches for n
> threads. This is an inefficient use of memory, it would be better to
> cache at a different level, eg memcached, redis, etc.

Hey,

The database queries (without cache) are fast enough. There are just too many. Sometimes
up to ten equal SELECT by primary key statements during one request. If you substitute
fast sql queries with fast cache access, I don't have a benefit.

> Secondly, this caching is very naïve. Queries are never evicted from

> the cache, ...

Yes, I know. The lines I wrote were just a prototype.

I want to use this for things that doesn't change for several days. The cache
could get purged by touching the wsgi-file, which reloads the wsgi worker processes.

Thomas

Malcolm Box

unread,
Nov 4, 2011, 2:32:58 PM11/4/11
to django...@googlegroups.com
On 4 November 2011 07:02, Thomas Guettler <h...@tbz-pariv.de> wrote:
Am 03.11.2011 18:42, schrieb Tom Evans:
> On Thu, Nov 3, 2011 at 2:22 PM, Thomas Guettler <h...@tbz-pariv.de> wrote:
>> Hi,
>>
>> I try to reduce the number of db-queries in my app.
>>
>> There is a model which changes almost never. It is like a "type of ticket"
>> in a trouble ticket system.
>>
>> On one page there are seven SQL-Queries (SELECT .... FROM ticket_type where id=123) which of course always return
>> the same result.

How are you getting 7 queries the same? Would select_related solve the problem by doing it all in one query?

Malcolm 
Reply all
Reply to author
Forward
0 new messages