You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by je...@apache.org on 2015/02/05 12:04:34 UTC

allura git commit: [#7824] ticket:721 Cache neighborhood record

Repository: allura
Updated Branches:
  refs/heads/ib/7824 [created] cfc01faa3


[#7824] ticket:721 Cache neighborhood record


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/cfc01faa
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/cfc01faa
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/cfc01faa

Branch: refs/heads/ib/7824
Commit: cfc01faa31ec88c3cf3a3454140bca623490a493
Parents: 78b0707
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 5 09:59:15 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Feb 5 09:59:15 2015 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/root.py   |  3 +-
 Allura/allura/lib/app_globals.py    | 34 +++++++++++++++
 Allura/allura/tests/test_globals.py | 74 +++++++++++++++++++++++++++++++-
 Allura/development.ini              |  4 ++
 4 files changed, 113 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/cfc01faa/Allura/allura/controllers/root.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/root.py b/Allura/allura/controllers/root.py
index e588d45..a0418d0 100644
--- a/Allura/allura/controllers/root.py
+++ b/Allura/allura/controllers/root.py
@@ -24,6 +24,7 @@ from tg import expose, request, config, session
 from tg.decorators import with_trailing_slash
 from tg.flash import TGFlash
 from pylons import tmpl_context as c
+from pylons import app_globals as g
 from paste.deploy.converters import asbool
 
 from allura.app import SitemapEntry
@@ -75,7 +76,7 @@ class RootController(WsgiDispatchController):
 
     def __init__(self):
         n_url_prefix = '/%s/' % request.path.split('/')[1]
-        n = M.Neighborhood.query.get(url_prefix=n_url_prefix)
+        n = g.neighborhood_cache.get(n_url_prefix)
         if n and not n.url_prefix.startswith('//'):
             n.bind_controller(self)
         self.browse = ProjectBrowseController()

http://git-wip-us.apache.org/repos/asf/allura/blob/cfc01faa/Allura/allura/lib/app_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index d196cc8..f3cb551 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -130,6 +130,36 @@ class ForgeMarkdown(markdown.Markdown):
         return html
 
 
+class NeighborhoodCache(object):
+    """Cached Neighborhood objects by url_prefix.
+    For faster RootController.__init__ lookup
+    """
+
+    def __init__(self, duration):
+        self.duration = duration
+        self._data = {}
+
+    def _lookup(self, url_prefix):
+        n = M.Neighborhood.query.get(url_prefix=url_prefix)
+        self._data[url_prefix] = {
+            'object': n,
+            'ts': datetime.datetime.utcnow(),
+        }
+        return n
+
+    def _expired(self, n):
+        delta = datetime.datetime.utcnow() - n['ts']
+        if delta >= datetime.timedelta(seconds=self.duration):
+            return True
+        return False
+
+    def get(self, url_prefix):
+        n = self._data.get(url_prefix)
+        if n and not self._expired(n):
+            return n['object']
+        return self._lookup(url_prefix)
+
+
 class Globals(object):
 
     """Container for objects available throughout the life of the application.
@@ -256,6 +286,10 @@ class Globals(object):
             macros=_cache_eps('allura.macros'),
         )
 
+        # Neighborhood cache
+        duration = asint(config.get('neighborhood.cache.duration', 0))
+        self.neighborhood_cache = NeighborhoodCache(duration)
+
         # Zarkov logger
         self._zarkov = None
 

http://git-wip-us.apache.org/repos/asf/allura/blob/cfc01faa/Allura/allura/tests/test_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_globals.py b/Allura/allura/tests/test_globals.py
index dd4e7e6..53c8af1 100644
--- a/Allura/allura/tests/test_globals.py
+++ b/Allura/allura/tests/test_globals.py
@@ -23,6 +23,7 @@ import os
 import allura
 import unittest
 import hashlib
+import datetime as dt
 from mock import patch, Mock
 
 from bson import ObjectId
@@ -41,7 +42,7 @@ from alluratest.controller import (
 
 from allura import model as M
 from allura.lib import helpers as h
-from allura.lib.app_globals import ForgeMarkdown
+from allura.lib.app_globals import ForgeMarkdown, NeighborhoodCache
 from allura.tests import decorators as td
 
 from forgewiki import model as WM
@@ -774,3 +775,74 @@ class TestHandlePaging(unittest.TestCase):
         self.assertEqual(g.handle_paging(None, 2, 30), (25, 2, 50))
         # handle paging must not mess up user preferences
         self.assertEqual(c.user.get_pref('results_per_page'), 25)
+
+
+class TestNeighborhoodCache(object):
+
+    @patch('allura.lib.app_globals.M', autospec=True)
+    @patch('allura.lib.app_globals.datetime', autospec=True)
+    def test_lookup(self, dt_mock, M):
+        dt_mock.datetime.utcnow.side_effect = [
+            dt.datetime(2015, 02, 05, 11, 32),
+            dt.datetime(2015, 02, 05, 11, 34),
+        ]
+        ret = M.Neighborhood.query.get.return_value
+        cache = NeighborhoodCache(30)
+        assert_equal(cache._data, {})
+
+        n = cache._lookup('/p/')
+        M.Neighborhood.query.get.assert_called_once_with(url_prefix='/p/')
+        assert_equal(n, ret)
+        assert_equal(cache._data, {'/p/': {
+            'object': ret,
+            'ts': dt.datetime(2015, 02, 05, 11, 32),
+        }})
+
+        # hits mongo every time
+        n = cache._lookup('/p/')
+        assert_equal(M.Neighborhood.query.get.call_count, 2)
+        assert_equal(n, ret)
+        assert_equal(cache._data, {'/p/': {
+            'object': ret,
+            'ts': dt.datetime(2015, 02, 05, 11, 34),
+        }})
+
+    @patch('allura.lib.app_globals.M', autospec=True)
+    @patch('allura.lib.app_globals.datetime', autospec=True)
+    def test_get(self, dt_mock, M):
+        dt_mock.datetime.utcnow.side_effect = [
+            dt.datetime(2015, 02, 05, 11, 32),
+            dt.datetime(2015, 02, 05, 11, 34),
+        ]
+        ret = M.Neighborhood.query.get.return_value
+        cache = NeighborhoodCache(30)
+        cache._expired = Mock(return_value=False)
+
+        n = cache.get('/p/')
+        M.Neighborhood.query.get.assert_called_once_with(url_prefix='/p/')
+        assert_equal(n, ret)
+
+        # don't hit mongo second time
+        n = cache.get('/p/')
+        assert_equal(M.Neighborhood.query.get.call_count, 1)
+        assert_equal(n, ret)
+
+        # and hits if cache is expired
+        cache._expired.return_value = True
+        n = cache.get('/p/')
+        assert_equal(M.Neighborhood.query.get.call_count, 2)
+        assert_equal(n, ret)
+
+    @patch('allura.lib.app_globals.datetime', autospec=True)
+    def test_expired(self, dt_mock):
+        dt_mock.timedelta = dt.timedelta  # restore original
+        _now = dt.datetime(2015, 02, 05, 11, 53)
+        dt_mock.datetime.utcnow.return_value = _now
+
+        cache = NeighborhoodCache(0)
+        assert_equal(cache._expired({'ts': _now}), True)
+        assert_equal(cache._expired({'ts': _now - dt.timedelta(seconds=1)}), True)
+
+        cache = NeighborhoodCache(30)
+        assert_equal(cache._expired({'ts': _now - dt.timedelta(seconds=29)}), False)
+        assert_equal(cache._expired({'ts': _now - dt.timedelta(seconds=30)}), True)

http://git-wip-us.apache.org/repos/asf/allura/blob/cfc01faa/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index e5be8b2..c6425a8 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -54,6 +54,10 @@ base_url = http://localhost:8080
 #lang = ru
 cache_dir = %(here)s/data
 
+# Cache Neighborhood objects for N seconds (speeds up requests).
+# Set to 0 to disable (the default).
+# neighborhood.cache.duration = 0
+
 ; Docs at http://beaker.readthedocs.org/en/latest/configuration.html#session-options
 ; and http://beaker.readthedocs.org/en/latest/modules/session.html#beaker.session.CookieSession
 beaker.session.key = allura