changeset 395:e10fa0d8e7ad

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.
author Brian Neal <bgneal@gmail.com>
date Fri, 25 Mar 2011 00:27:10 +0000 (2011-03-25)
parents 2cb0cc334c50
children 63e4682d6482
files gpp/forums/management/commands/update_forum_stats.py gpp/forums/stats.py gpp/forums/templatetags/forum_tags.py
diffstat 3 files changed, 78 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- /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()
--- /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
--- 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,