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/16 12:44:38 UTC
[07/37] allura git commit: [#7824] ticket:721 Cache neighborhood
record
[#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/724ba595
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/724ba595
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/724ba595
Branch: refs/heads/ib/4542
Commit: 724ba5951657b96967209a69d7ee9d324c6699b2
Parents: 09365c9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 5 09:59:15 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 9 07:47:57 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/724ba595/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/724ba595/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/724ba595/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/724ba595/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