[CalendarServer-changes] [11021] CalendarServer/trunk/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Tue Apr 9 18:50:17 PDT 2013
Revision: 11021
http://trac.calendarserver.org//changeset/11021
Author: cdaboo at apple.com
Date: 2013-04-09 18:50:16 -0700 (Tue, 09 Apr 2013)
Log Message:
-----------
Make sure that sync-token in PROPFIND Depth:1 on home changes for sharer and sharee when event changes in shared calendar.
Modified Paths:
--------------
CalendarServer/trunk/twistedcaldav/cache.py
CalendarServer/trunk/twistedcaldav/method/propfind.py
CalendarServer/trunk/twistedcaldav/storebridge.py
Modified: CalendarServer/trunk/twistedcaldav/cache.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/cache.py 2013-04-10 01:42:06 UTC (rev 11020)
+++ CalendarServer/trunk/twistedcaldav/cache.py 2013-04-10 01:50:16 UTC (rev 11021)
@@ -20,7 +20,7 @@
from zope.interface import implements
-from twisted.internet.defer import succeed, maybeDeferred, inlineCallbacks,\
+from twisted.internet.defer import succeed, maybeDeferred, inlineCallbacks, \
returnValue
from twext.web2.dav.util import allDataFromStream
from twext.web2.http import Response
@@ -50,15 +50,15 @@
- directoryToken - a hash of that principal's directory record
- uriToken - a token for the request uri
- childTokens - tokens for any child resources the request uri depends on (for depth:1)
-
+
The current principalToken, uriToken and childTokens values are themselves stored in the cache using the key prefix 'cacheToken:'.
When the 'changeCache' api is called the cached value for the matching token is updated.
-
+
(4) When a request is being checked in the cache, the response cache entry key is first computed and any value extracted. The
tokens in the value are then checked against the current set of tokens in the cache. If there is any mismatch between tokens, the
cache entry is considered invalid and the cached response is not returned. If everything matches up, the cached response is returned
to the caller and ultimately sent directly back to the client.
-
+
(5) Because of shared calendars/address books that can affect the calendar/address book homes of several different users at once, we
need to keep track of the separate childTokens for each child resource. The tokens for shared resources are keyed of the sharer's uri,
so sharee's homes use that token. That way a single token for all shared instances is used and changed just once.
@@ -72,18 +72,22 @@
def __init__(self, *args, **kwargs):
pass
+
def changed(self):
return succeed(None)
+
class DisabledCache(object):
def getResponseForRequest(self, request):
return succeed(None)
+
def cacheResponseForRequest(self, request, response):
return succeed(response)
+
class URINotFoundException(Exception):
def __init__(self, uri):
self.uri = uri
@@ -95,6 +99,7 @@
self.uri)
+
class MemcacheChangeNotifier(LoggingMixIn, CachePoolUserMixIn):
def __init__(self, resource, cachePool=None, cacheHandle="Default"):
@@ -102,22 +107,31 @@
self._cachePool = cachePool
self._cachePoolHandle = cacheHandle
+
def _newCacheToken(self):
return str(uuid.uuid4())
+
def changed(self):
"""
Change the cache token for a resource
return: A L{Deferred} that fires when the token has been changed.
"""
- url = self._resource.url()
+ # For shared resources we use the owner URL as the cache key
+ if hasattr(self._resource, "owner_url"):
+ url = self._resource.owner_url()
+ else:
+ url = self._resource.url()
+
self.log_debug("Changing Cache Token for %r" % (url,))
return self.getCachePool().set(
'cacheToken:%s' % (url,),
- self._newCacheToken(), expireTime=config.ResponseCacheTimeout*60)
-
+ self._newCacheToken(), expireTime=config.ResponseCacheTimeout * 60)
+
+
+
class BaseResponseCache(LoggingMixIn):
"""
A base class which provides some common operations
@@ -149,10 +163,10 @@
def _canonicalizeURIForRequest(self, uri, request):
"""
Always use canonicalized forms of the URIs for caching (i.e. __uids__ paths).
-
- Do this without calling locateResource which may cause a query on the store.
+
+ Do this without calling locateResource which may cause a query on the store.
"""
-
+
uribits = uri.split("/")
if len(uribits) > 1 and uribits[1] in ("principals", "calendars", "addressbooks"):
if uribits[2] == "__uids__":
@@ -166,8 +180,7 @@
uribits[2] = "__uids__"
uribits[3] = record.uid
return succeed("/".join(uribits))
-
-
+
# Fall back to the locateResource approach
try:
return request.locateResource(uri).addCallback(
@@ -190,6 +203,7 @@
return d
+
@inlineCallbacks
def _requestKey(self, request):
"""
@@ -201,7 +215,7 @@
# Give it back to the request so it can be read again
request.stream = MemoryStream(requestBody)
request.stream.doStartReading = None
-
+
# Normalize the property order by doing a "dumb" sort on lines
requestLines = requestBody.splitlines()
requestLines.sort()
@@ -222,6 +236,7 @@
return d1
+
class MemcacheResponseCache(BaseResponseCache, CachePoolUserMixIn):
def __init__(self, docroot, cachePool=None):
self._docroot = docroot
@@ -238,6 +253,7 @@
else:
return self.getCachePool().get('cacheToken:%s' % (uri,))
+
@inlineCallbacks
def _tokenForRecord(self, uri, request):
"""
@@ -247,19 +263,21 @@
record = (yield self._getRecordForURI(uri, request))
returnValue(record.cacheToken())
+
@inlineCallbacks
def _tokensForChildren(self, rURI, request):
"""
Create a dict of child resource tokens for any "recorded" during this request in the childCacheURIs attribute.
"""
-
+
if hasattr(request, "childCacheURIs"):
tokens = dict([(uri, (yield self._tokenForURI(uri)),) for uri in request.childCacheURIs])
returnValue(tokens)
else:
returnValue({})
-
- @inlineCallbacks
+
+
+ @inlineCallbacks
def _getTokens(self, request):
"""
Tokens are a principal token, directory record token, resource token and list
@@ -296,19 +314,19 @@
"""
try:
key = (yield self._hashedRequestKey(request))
-
+
self.log_debug("Checking cache for: %r" % (key,))
_ignore_flags, value = (yield self.getCachePool().get(key))
-
+
if value is None:
self.log_debug("Not in cache: %r" % (key,))
returnValue(None)
-
+
self.log_debug("Found in cache: %r = %r" % (key, value))
-
+
(principalToken, directoryToken, uriToken, childTokens, (code, headers, body)) = cPickle.loads(value)
currentTokens = (yield self._getTokens(request))
-
+
if currentTokens[0] != principalToken:
self.log_debug(
"Principal token doesn't match for %r: %r != %r" % (
@@ -316,7 +334,7 @@
currentTokens[0],
principalToken))
returnValue(None)
-
+
if currentTokens[1] != directoryToken:
self.log_debug(
"Directory Record Token doesn't match for %r: %r != %r" % (
@@ -324,7 +342,7 @@
currentTokens[1],
directoryToken))
returnValue(None)
-
+
if currentTokens[2] != uriToken:
self.log_debug(
"URI token doesn't match for %r: %r != %r" % (
@@ -332,7 +350,7 @@
currentTokens[2],
uriToken))
returnValue(None)
-
+
for childuri, token in childTokens.items():
currentToken = (yield self._tokenForURI(childuri))
if currentToken != token:
@@ -343,19 +361,20 @@
currentToken,
token))
returnValue(None)
-
+
r = Response(code,
stream=MemoryStream(body))
-
+
for key, value in headers.iteritems():
r.headers.setRawHeaders(key, value)
-
+
returnValue(r)
except URINotFoundException, e:
self.log_debug("Could not locate URI: %r" % (e,))
returnValue(None)
+
@inlineCallbacks
def cacheResponseForRequest(self, request, response):
"""
@@ -368,9 +387,9 @@
key = request.cacheKey
else:
key = (yield self._hashedRequestKey(request))
-
+
key, responseBody = (yield self._getResponseBody(key, response))
-
+
response.headers.removeHeader('date')
response.stream = MemoryStream(responseBody)
pToken, dToken, uToken, cTokens = (yield self._getTokens(request))
@@ -388,30 +407,34 @@
))
self.log_debug("Adding to cache: %r = %r" % (key, cacheEntry))
yield self.getCachePool().set(key, cacheEntry,
- expireTime=config.ResponseCacheTimeout*60)
+ expireTime=config.ResponseCacheTimeout * 60)
except URINotFoundException, e:
self.log_debug("Could not locate URI: %r" % (e,))
- returnValue(response)
+ returnValue(response)
+
class _CachedResponseResource(object):
implements(IResource)
def __init__(self, response):
self._response = response
+
def renderHTTP(self, request):
if not hasattr(request, "extendedLogItems"):
request.extendedLogItems = {}
request.extendedLogItems["cached"] = "1"
return self._response
+
def locateChild(self, request, segments):
return self, []
+
class PropfindCacheMixin(object):
"""
A mixin that causes a resource's PROPFIND response to be cached. It also adds an api to change the
@@ -433,12 +456,15 @@
d.addCallback(_getResponseCache)
return d
+
def changeCache(self):
if hasattr(self, 'cacheNotifier'):
return self.cacheNotifier.changed()
else:
self.log_debug("%r does not have a cacheNotifier but was changed" % (self,))
+
+
class ResponseCacheMixin(object):
"""
This is a mixin for a child resource that does not itself cache PROPFINDs, but needs to invalidate a parent
@@ -451,19 +477,25 @@
else:
self.log_debug("%r does not have a cacheNotifier but was changed" % (self,))
+
+
class CacheStoreNotifier(object):
-
+
def __init__(self, resource):
self.resource = resource
-
+
+
def notify(self, op="update"):
self.resource.changeCache()
+
def clone(self, label="default", id=None):
return self
+
def getID(self, label="default"):
return None
+
def nodeName(self, label="default"):
return succeed(None)
Modified: CalendarServer/trunk/twistedcaldav/method/propfind.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/propfind.py 2013-04-10 01:42:06 UTC (rev 11020)
+++ CalendarServer/trunk/twistedcaldav/method/propfind.py 2013-04-10 01:50:16 UTC (rev 11021)
@@ -219,8 +219,11 @@
# This needed for propfind cache tracking of children changes
if depth == "1":
- if resource != self and hasattr(resource, "url"):
- request.childCacheURIs.append(resource.url())
+ if resource != self:
+ if hasattr(resource, "owner_url"):
+ request.childCacheURIs.append(resource.owner_url())
+ elif hasattr(resource, "url"):
+ request.childCacheURIs.append(resource.url())
else:
xml_response = davxml.StatusResponse(davxml.HRef(uri), davxml.Status.fromResponseCode(responsecode.FORBIDDEN))
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2013-04-10 01:42:06 UTC (rev 11020)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2013-04-10 01:50:16 UTC (rev 11021)
@@ -265,6 +265,13 @@
return joinURL(self._parentResource.url(), self._name, "/")
+ def owner_url(self):
+ if self.isShareeCollection():
+ return joinURL(self._share.url(), "/")
+ else:
+ return self.url()
+
+
def parentResource(self):
return self._parentResource
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130409/3e47f08e/attachment-0001.html>
More information about the calendarserver-changes
mailing list