[CalendarServer-changes] [2361]
CalendarServer/branches/propfind-cache/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Fri May 2 10:19:35 PDT 2008
Revision: 2361
http://trac.macosforge.org/projects/calendarserver/changeset/2361
Author: dreid at apple.com
Date: 2008-05-02 10:19:31 -0700 (Fri, 02 May 2008)
Log Message:
-----------
Drastically simplified Cache implementation, need to refactor notifier implementation so there is only one token.
Modified Paths:
--------------
CalendarServer/branches/propfind-cache/twistedcaldav/cache.py
CalendarServer/branches/propfind-cache/twistedcaldav/test/test_cache.py
Modified: CalendarServer/branches/propfind-cache/twistedcaldav/cache.py
===================================================================
--- CalendarServer/branches/propfind-cache/twistedcaldav/cache.py 2008-05-01 18:04:50 UTC (rev 2360)
+++ CalendarServer/branches/propfind-cache/twistedcaldav/cache.py 2008-05-02 17:19:31 UTC (rev 2361)
@@ -18,8 +18,12 @@
import time
import os
+from zope.interface import implements
+
from twisted.python.filepath import FilePath
+from twisted.python import log
+from twisted.web2.iweb import IResource
from twisted.web2.dav import davxml
from twisted.web2.http import HTTPError
from twisted.web2.dav.xattrprops import xattrPropertyStore
@@ -80,100 +84,99 @@
-class CacheChangeObserver(CacheChangeLoaderMixin):
- def __init__(self, propertyStore):
- self._propertyStore = propertyStore
- self._oldPropToken = None
- self._oldDataToken = None
+class ResponseCache(object):
+ """
+ An object that caches responses to given requests.
+ @ivar CACHE_TIMEOUT: The number of seconds that a cache entry is valid,
+ (default 3600 seconds or 1 hour).
- def propertiesHaveChanged(self):
- self._loadTokens()
+ @ivar _docroot: An L{FilePath} that points to the document root.
+ @ivar _responses: A C{dict} with (request-method, request-uri,
+ principal-uri) keys and (principal-token, uri-token, cache-time,
+ response) values.
+ """
- if self._propToken != self._oldPropToken:
- self._oldPropToken = self._propToken
- return True
+ CACHE_TIMEOUT = 60*60 #1hr
- return False
+ def __init__(self, docroot):
+ self._docroot = docroot
+ self._responses = {}
- def dataHasChanged(self):
- self._loadTokens()
+ def _tokenForURI(self, uri):
+ """
+ Get a property store for the given C{uri}.
- if self._dataToken != self._oldDataToken:
- self._oldDataToken = self._dataToken
- return True
+ @param uri: The URI we'd like the token for.
+ @return: A C{str} representing the token for the URI.
+ """
- return False
+ class __FauxStaticResource(object):
+ def __init__(self, fp):
+ self.fp = fp
+ fp = self._docroot
+ for childPath in uri.split('/')[:4]:
+ fp = fp.child(childPath)
-class PropfindCachingResource(object):
- CACHE_TIMEOUT = 60*60 # 1 hour
+ props = self.propertyStoreFactory(__FauxStaticResource(fp))
- propertyStoreFactory = xattrPropertyStore
- observerFactory = CacheChangeObserver
+ try:
+ tokenElement = props.get(CacheTokensProperty.qname())
+ return tokenElement.children[0].data
- def __init__(self, docroot, timeFunc=time.time):
- self._docroot = docroot
- self._responses = {}
- self._observers = {}
- self._timeFunc = timeFunc
+ except HTTPError, err:
+ pass
- def _tokenPathForURI(self, uri):
- tokenPath = self._docroot
+ def _time(self):
+ """
+ Return the current time in seconds since the epoch
+ """
+ return time.time()
- for childName in uri.split('/')[:4]:
- tokenPath = tokenPath.child(childName)
- return tokenPath
+ def getResponseForRequest(self, request):
+ """
+ Retrieve a cached response to the given C{request} otherwise return
+ C{None}
+ @param request: An L{IRequest} provider that will be used to locate
+ a cached L{IResponse}.
- def _observerForURI(self, uri):
- class FauxStaticResource(object):
- def __init__(self, fp):
- self.fp = fp
+ @return: An L{IResponse} or C{None} if the response has not been cached.
+ """
+ key = (request.method, request.uri, request.authnUser)
+ if key not in self._responses:
+ return None
- propertyStore = self.propertyStoreFactory(
- FauxStaticResource(self._tokenPathForURI(uri)))
+ principalToken, uriToken, cacheTime, response = self._responses[key]
- return self.observerFactory(propertyStore)
+ if self._tokenForURI(request.authnUser) != principalToken:
+ return None
+ elif self._tokenForURI(request.uri) != uriToken:
+ return None
- def _cacheResponse(self, request, response):
- if getattr(request, 'cacheRequest', False):
- if request.uri not in self._observers:
- self._observers[request.uri] = self._observerForURI(request.uri)
+ elif self._time() >= cacheTime + self.CACHE_TIMEOUT:
+ return None
- self._responses[(request.method,
- request.uri,
- request.authnUser)] = (self._timeFunc(),
- response)
-
return response
- def renderHTTP(self, request):
- key = (request.method, request.uri, request.authnUser)
+ def cacheResponseForRequest(self, request, response):
+ """
+ Cache the given C{response} for the given C{request}.
- if key in self._responses:
- cacheTime, cachedResponse = self._responses[key]
- if cacheTime + CACHE_TIMEOUT <= self._timeFunc():
- if (request.uri in self._observers and
- self._observers[request.uri]()):
+ @param request: An L{IRequest} provider that will be keyed to the
+ given C{response}.
- return cachedResponse
-
- def _renderResource(resource, request):
- request.addResponseFilter(self._cacheResponse)
- return resource.renderHTTP(request)
-
- request.notInCache = True
- d = request.locateResource(request.uri)
- d.addCallback(_renderResource, request)
- return d
-
-
- def locateChild(self, request, segments):
- return self, []
+ @param response: An L{IResponse} provider that will be returned on
+ subsequent checks for the given L{IRequest}
+ """
+ key = (request.method, request.uri, request.authnUser)
+ self._responses[key] = (self._tokenForURI(request.authnUser),
+ self._tokenForURI(request.uri),
+ self._time(), response)
Modified: CalendarServer/branches/propfind-cache/twistedcaldav/test/test_cache.py
===================================================================
--- CalendarServer/branches/propfind-cache/twistedcaldav/test/test_cache.py 2008-05-01 18:04:50 UTC (rev 2360)
+++ CalendarServer/branches/propfind-cache/twistedcaldav/test/test_cache.py 2008-05-02 17:19:31 UTC (rev 2361)
@@ -22,8 +22,7 @@
from twistedcaldav.cache import CacheChangeNotifier
from twistedcaldav.cache import CacheTokensProperty
-from twistedcaldav.cache import CacheChangeObserver
-from twistedcaldav.cache import PropfindCachingResource
+from twistedcaldav.cache import ResponseCache
from twistedcaldav.test.util import InMemoryPropertyStore
@@ -40,22 +39,6 @@
-class StubCacheChangeObserver(CacheChangeObserver):
- def __init__(self, ps):
- self._ps = ps
- self.fp = self._ps.resource.fp
-
- self._oldPropToken = None
- self._oldDataToken = None
- self.curPropToken = None
- self.curDataToken = None
-
- def _loadTokens(self):
- self._dataToken = self.curDataToken
- self._propToken = self.curPropToken
-
-
-
class StubRequest(object):
def __init__(self, method, uri, authnUser):
self.method = method
@@ -108,139 +91,108 @@
-class CacheChangeObserverTests(TestCase):
+class ResponseCacheTests(TestCase):
def setUp(self):
- self.props = InMemoryPropertyStore()
- self.props._properties[CacheTokensProperty.qname()
- ] = CacheTokensProperty.fromString(
- 'propToken0:dataToken0')
- self.observer = CacheChangeObserver(self.props)
+ self.tokens = {
+ '/calendars/users/cdaboo/': 'uriToken0',
+ '/principals/users/cdaboo/': 'principalToken0',
+ '/principals/users/dreid/': 'principalTokenX'}
+ self.rc = ResponseCache(None)
+ self.rc._tokenForURI = self.tokens.get
- def test_propertiesHaveChangedNewObserver(self):
- self.assertEquals(self.observer.propertiesHaveChanged(), True)
+ self.rc._time = (lambda:0)
+ self.expected_response = object()
- def test_propertiesHaveChanged(self):
- self.assertEquals(self.observer.propertiesHaveChanged(), True)
+ self.rc._responses[(
+ 'PROPFIND',
+ '/calendars/users/cdaboo/',
+ '/principals/users/cdaboo/')] = (
+ 'principalToken0', 'uriToken0', 0, self.expected_response)
- self.props._properties[CacheTokensProperty.qname()
- ] = CacheTokensProperty.fromString(
- 'propToken1:dataToken0')
- self.assertEquals(self.observer.propertiesHaveChanged(), True)
+ def test_getResponseForRequestNotInCache(self):
+ response = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/users/dreid/',
+ '/principals/users/dreid/'))
+ self.assertEquals(response, None)
- def test_propertiesHaveNotChanged(self):
- self.assertEquals(self.observer.propertiesHaveChanged(), True)
- self.assertEquals(self.observer.propertiesHaveChanged(), False)
+ def test_getResponseForRequestInCache(self):
+ response = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/users/cdaboo/',
+ '/principals/users/cdaboo/'))
- def test_propertiesDoNotChangeData(self):
- self.assertEquals(self.observer.propertiesHaveChanged(), True)
- self.assertEquals(self.observer.dataHasChanged(), True)
+ self.assertEquals(self.expected_response, response)
- self.props._properties[CacheTokensProperty.qname()
- ] = CacheTokensProperty.fromString(
- 'propToken1:dataToken0')
- self.assertEquals(self.observer.propertiesHaveChanged(), True)
- self.assertEquals(self.observer.dataHasChanged(), False)
+ def test_getResponseForRequestPrincipalTokenChanged(self):
+ self.tokens['/principals/users/cdaboo/'] = 'principalToken1'
+ response = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/users/cdaboo/',
+ '/principals/users/cdaboo/'))
- def test_dataHasChanged(self):
- self.assertEquals(self.observer.dataHasChanged(), True)
+ self.assertEquals(response, None)
- self.props._properties[CacheTokensProperty.qname()
- ] = CacheTokensProperty.fromString(
- 'propToken0:dataToken1')
- self.assertEquals(self.observer.dataHasChanged(), True)
+ def test_getResponseForRequestUriTokenChanged(self):
+ self.tokens['/calendars/users/cdaboo/'] = 'uriToken1'
+ response = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/users/cdaboo/',
+ '/principals/users/cdaboo/'))
- def test_dataHasChangedNewObserver(self):
- self.assertEquals(self.observer.dataHasChanged(), True)
+ self.assertEquals(response, None)
- def test_dataHasNotChanged(self):
- self.assertEquals(self.observer.dataHasChanged(), True)
- self.assertEquals(self.observer.dataHasChanged(), False)
+ def test_getResponseForRequestCacheTimeoutLapsed(self):
+ self.rc._time = (lambda: 50000)
+ response = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/users/cdaboo/',
+ '/principals/users/cdaboo/'))
- def test_dataDoesNotChangeProperties(self):
- self.assertEquals(self.observer.dataHasChanged(), True)
- self.assertEquals(self.observer.propertiesHaveChanged(), True)
+ self.assertEquals(response, None)
- self.props._properties[CacheTokensProperty.qname()
- ] = CacheTokensProperty.fromString(
- 'propToken0:dataToken1')
- self.assertEquals(self.observer.dataHasChanged(), True)
- self.assertEquals(self.observer.propertiesHaveChanged(), False)
+ def test_cacheResponseForRequest(self):
+ expected_response = object()
+ self.rc.cacheResponseForRequest(StubRequest('PROPFIND',
+ '/principals/users/dreid/',
+ '/principals/users/dreid/'),
+ expected_response)
+ response = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/principals/users/dreid/',
+ '/principals/users/dreid/'))
+ self.assertEquals(response, expected_response)
-class PropfindCachingResourceTests(TestCase):
- def setUp(self):
- self.pcr = PropfindCachingResource(FilePath('/root'),
- timeFunc=lambda:0)
- def test_tokenPathForURI(self):
- paths = [
- ('/principals/__uids__/557C330A-06E2-403B-BC24-CE3A253CDB5B/',
- '/root/principals/__uids__/557C330A-06E2-403B-BC24-CE3A253CDB5B'),
- ('/calendars/users/dreid/', '/root/calendars/users/dreid'),
- ('/calendars/users/dreid/calendar', '/root/calendars/users/dreid')]
+ def test__tokenForURI(self):
+ docroot = FilePath(self.mktemp())
+ principal = docroot.child('principals').child('users').child('wsanchez')
- for inPath, outPath in paths:
- self.assertEquals(self.pcr._tokenPathForURI(inPath).path, outPath)
+ expected_token = "wsanchezToken0"
+ props = InMemoryPropertyStore()
+ props._properties[CacheTokensProperty.qname()
+ ] = CacheTokensProperty.fromString(expected_token)
- def test_observerForURI(self):
- self.pcr.observerFactory = StubCacheChangeObserver
+ stores = {principal.path: props}
- paths = [
- ('/principals/__uids__/557C330A-06E2-403B-BC24-CE3A253CDB5B/',
- '/root/principals/__uids__/557C330A-06E2-403B-BC24-CE3A253CDB5B'),
- ('/calendars/users/dreid/', '/root/calendars/users/dreid'),
- ('/calendars/users/dreid/calendar', '/root/calendars/users/dreid')]
+ rc = ResponseCache(docroot)
- for inPath, outPath in paths:
- self.assertEquals(self.pcr._observerForURI(inPath).fp.path,
- outPath)
+ rc.propertyStoreFactory = (lambda rsrc: stores[rsrc.fp.path])
-
- def test_cacheResponseTaggedRequestTrue(self):
- response = object()
- request = StubRequest('GET', '/root/calendars/users/dreid', 'dreid')
-
- request.cacheRequest = True
-
- r = self.pcr._cacheResponse(request, response)
- self.assertEquals(r, response)
-
- self.assertEquals(self.pcr._responses,
- {('GET',
- '/root/calendars/users/dreid',
- 'dreid'): (0, response)})
-
-
- def test_cacheResponseTaggedRequestFalse(self):
- response = object()
- request = StubRequest('GET', '/root/calendars/users/dreid', 'dreid')
-
- r = self.pcr._cacheResponse(request, response)
- self.assertEquals(r, response)
-
- self.assertEquals(self.pcr._responses, {})
-
-
- def test_cacheResposneUntaggedRequest(self):
- response = object()
- request = StubRequest('GET', '/root/calendars/users/dreid', 'dreid')
- del request.cacheRequest
-
- r = self.pcr._cacheResponse(request, response)
- self.assertEquals(r, response)
-
- self.assertEquals(self.pcr._responses, {})
+ token = rc._tokenForURI('/principals/users/wsanchez')
+ self.assertEquals(token, expected_token)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080502/ad6b1008/attachment-0001.html
More information about the calendarserver-changes
mailing list