# HG changeset patch # User Brian Neal # Date 1301012830 0 # Node ID e10fa0d8e7ad4f9ee42905ec0a59371c1d67b53c # Parent 2cb0cc334c5085e0585e3ce3b4bfec7b1b2798f6 Fixing #192; cache some forum stats and have them be computed outside of the request/response cycle. Update the template tag to just read the cache. diff -r 2cb0cc334c50 -r e10fa0d8e7ad gpp/forums/management/commands/update_forum_stats.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/forums/management/commands/update_forum_stats.py Fri Mar 25 00:27:10 2011 +0000 @@ -0,0 +1,17 @@ +""" +update_forum_stats.py - A management command to calculate and update the +cache with the forum statistics. These are done out of the request / +response cycle because doing a count on the Post table is expensive +under MySQL and InnoDb. + +""" +from django.core.management.base import NoArgsCommand, CommandError + +from forums.stats import update_stats + + +class Command(NoArgsCommand): + help = "Calculates and updates the cache with forums statistics" + + def handle_noargs(self, **opts): + update_stats() diff -r 2cb0cc334c50 -r e10fa0d8e7ad gpp/forums/stats.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/forums/stats.py Fri Mar 25 00:27:10 2011 +0000 @@ -0,0 +1,59 @@ +""" +This module is responsible for managing various forum statistics. + +""" +from django.core.cache import cache +from django.contrib.auth.models import User + +from forums.models import Post + + +CACHE_KEY = 'forums-stats' +CACHE_TIMEOUT = 4 * 60 * 60 # seconds + + +def calc_stats(): + """ + This function is responsible for computing the forum statistics. + A tuple is returned: (forums post count, active user count, latest username) + + """ + post_count = Post.objects.all().count() + user_count = User.objects.all().count() + + if user_count > 0: + latest_user = User.objects.filter(is_active=True).values_list( + 'username', flat=True).order_by('-date_joined')[0] + else: + latest_user = None + + return post_count, user_count, latest_user + + +def update_stats(): + """ + This function is responsible for computing the forum statistics and + inserting them into the cache. The stats are returned. + + This function should be run periodically, preferably outside of the + request/response cycle. On MySQL under InnoDb it is expensive to retrieve + the total post count. + + """ + stats = calc_stats() + cache.set(CACHE_KEY, stats, CACHE_TIMEOUT) + return stats + + +def retrieve_stats(): + """ + This function retrieves the forum stats from the cache if they are + available. If there is a cache-miss, the stats are calcuated, the cache is + updated, and the stats returned. + + """ + stats = cache.get(CACHE_KEY) + if stats is None: + stats = update_stats() + + return stats diff -r 2cb0cc334c50 -r e10fa0d8e7ad gpp/forums/templatetags/forum_tags.py --- a/gpp/forums/templatetags/forum_tags.py Thu Mar 24 00:49:21 2011 +0000 +++ b/gpp/forums/templatetags/forum_tags.py Fri Mar 25 00:27:10 2011 +0000 @@ -13,6 +13,7 @@ from forums.models import Topic from forums.models import Post from forums.models import Category +from forums.stats import retrieve_stats from core.models import Statistic @@ -158,14 +159,7 @@ except Statistic.DoesNotExist: stats = None - post_count = Post.objects.all().count() - user_count = User.objects.all().count() - - if user_count > 0: - latest_user = User.objects.filter(is_active=True).values_list( - 'username', flat=True).order_by('-date_joined')[0] - else: - latest_user = None + post_count, user_count, latest_user = retrieve_stats() return { 'stats': stats,