[Checkins] SVN: lovely.memcached/trunk/src/lovely/memcached/ Added
an event handler for invalidation events.
Jürgen Kartnaller
juergen at kartnaller.at
Tue May 8 07:03:50 EDT 2007
Log message for revision 75621:
Added an event handler for invalidation events.
Added a memcache client implementation for testing, this client doesn't need
a running memcached instance.
Changed:
U lovely.memcached/trunk/src/lovely/memcached/README.txt
U lovely.memcached/trunk/src/lovely/memcached/browser/README.txt
U lovely.memcached/trunk/src/lovely/memcached/configure.zcml
A lovely.memcached/trunk/src/lovely/memcached/event.py
U lovely.memcached/trunk/src/lovely/memcached/interfaces.py
A lovely.memcached/trunk/src/lovely/memcached/testing/
A lovely.memcached/trunk/src/lovely/memcached/testing/README.txt
A lovely.memcached/trunk/src/lovely/memcached/testing/__init__.py
A lovely.memcached/trunk/src/lovely/memcached/testing/memcache.py
U lovely.memcached/trunk/src/lovely/memcached/tests.py
U lovely.memcached/trunk/src/lovely/memcached/utility.py
-=-
Modified: lovely.memcached/trunk/src/lovely/memcached/README.txt
===================================================================
--- lovely.memcached/trunk/src/lovely/memcached/README.txt 2007-05-08 08:15:40 UTC (rev 75620)
+++ lovely.memcached/trunk/src/lovely/memcached/README.txt 2007-05-08 11:03:48 UTC (rev 75621)
@@ -253,3 +253,52 @@
>>> util.set('notStored', 'ignored') is None
True
+
+Invalidationevents
+==================
+
+Events can be used to create invalidations. The event handler invalidates in
+registered memcached utilities.
+
+ >>> from zope import component
+ >>> from lovely.memcached.interfaces import IMemcachedClient
+ >>> cacheUtil1 = MemcachedClient()
+ >>> component.provideUtility(cacheUtil1, IMemcachedClient, name='cacheUtil1')
+ >>> cacheUtil1.set('Value1', 'key1', dependencies=['dep1'])
+ '19192ccdbb8267c35b9bdaf2f1f5594b'
+
+ >>> cacheUtil1.query('key1')
+ 'Value1'
+
+ >>> from lovely.memcached.event import InvalidateCacheEvent
+ >>> ev = InvalidateCacheEvent(dependencies=['dep1'])
+
+ >>> from lovely.memcached.event import invalidateCache
+ >>> invalidateCache(ev)
+ >>> cacheUtil1.query('key1') is None
+ True
+
+With more than one memcache utility we can invalidate in all utilities.
+
+ >>> from lovely.memcached.testing import TestMemcachedClient
+ >>> cacheUtil2 = TestMemcachedClient()
+ >>> component.provideUtility(cacheUtil2, IMemcachedClient, name='cacheUtil2')
+ >>> key = cacheUtil1.set('Value1', 'key1', dependencies=['dep1'])
+ >>> key = cacheUtil2.set('Value2', 'key2', dependencies=['dep1'])
+ >>> invalidateCache(InvalidateCacheEvent(dependencies=['dep1']))
+ >>> cacheUtil1.query('key1') is None
+ True
+ >>> cacheUtil2.query('key2') is None
+ True
+
+Or we specify in which memcache we want to invalidate.
+
+ >>> key = cacheUtil1.set('Value1', 'key1', dependencies=['dep1'])
+ >>> key = cacheUtil2.set('Value2', 'key2', dependencies=['dep1'])
+ >>> invalidateCache(InvalidateCacheEvent(cacheName='cacheUtil1',
+ ... dependencies=['dep1']))
+ >>> cacheUtil1.query('key1') is None
+ True
+ >>> cacheUtil2.query('key2') is None
+ False
+
Modified: lovely.memcached/trunk/src/lovely/memcached/browser/README.txt
===================================================================
--- lovely.memcached/trunk/src/lovely/memcached/browser/README.txt 2007-05-08 08:15:40 UTC (rev 75620)
+++ lovely.memcached/trunk/src/lovely/memcached/browser/README.txt 2007-05-08 11:03:48 UTC (rev 75621)
@@ -1,6 +1,6 @@
-==============================
-Memcached utilit browser views
-==============================
+===============================
+Memcached utility browser views
+===============================
Let us add a memcached utility.
Modified: lovely.memcached/trunk/src/lovely/memcached/configure.zcml
===================================================================
--- lovely.memcached/trunk/src/lovely/memcached/configure.zcml 2007-05-08 08:15:40 UTC (rev 75620)
+++ lovely.memcached/trunk/src/lovely/memcached/configure.zcml 2007-05-08 11:03:48 UTC (rev 75621)
@@ -23,6 +23,8 @@
<adapter factory=".configurator.SetUpMemcachedClient"
zcml:condition="installed z3c.configurator"
name="lovely.memcachedclient"/>
+
+ <subscriber handler=".event.invalidateCache"/>
<include package=".browser"/>
Added: lovely.memcached/trunk/src/lovely/memcached/event.py
===================================================================
--- lovely.memcached/trunk/src/lovely/memcached/event.py 2007-05-08 08:15:40 UTC (rev 75620)
+++ lovely.memcached/trunk/src/lovely/memcached/event.py 2007-05-08 11:03:48 UTC (rev 75621)
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2006 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+from zope import interface
+from zope import component
+
+from interfaces import IInvalidateCacheEvent, IMemcachedClient
+
+
+class InvalidateCacheEvent(object):
+ interface.implements(IInvalidateCacheEvent)
+
+ def __init__(self,
+ cacheName=None, key=None, ns=None, raw=False, dependencies=[]):
+ self.cacheName = cacheName
+ self.key = key
+ self.ns = ns
+ self.raw = raw
+ self.dependencies = dependencies
+
+
+ at component.adapter(IInvalidateCacheEvent)
+def invalidateCache(event):
+ if event.cacheName is not None:
+ cache = component.queryUtility(IMemcachedClient, event.cacheName)
+ caches = []
+ if cache is not None:
+ caches.append(cache)
+ else:
+ caches = component.getAllUtilitiesRegisteredFor(IMemcachedClient)
+ for cache in caches:
+ cache.invalidate(event.key, event.ns, event.raw, event.dependencies)
+
Property changes on: lovely.memcached/trunk/src/lovely/memcached/event.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: lovely.memcached/trunk/src/lovely/memcached/interfaces.py
===================================================================
--- lovely.memcached/trunk/src/lovely/memcached/interfaces.py 2007-05-08 08:15:40 UTC (rev 75620)
+++ lovely.memcached/trunk/src/lovely/memcached/interfaces.py 2007-05-08 11:03:48 UTC (rev 75621)
@@ -20,7 +20,7 @@
from zope import schema
class IMemcachedClient(interface.Interface):
-
+
"""A memcache client utility"""
defaultNS = schema.TextLine(
@@ -28,7 +28,7 @@
description=u"The default namespace used by this client",
required=False,
default=None)
-
+
servers = schema.List(
title = u'Servers',
description = u"Servers defined as <hostname>:<port>",
@@ -36,7 +36,7 @@
required = True,
default=['127.0.0.1:11211']
)
-
+
defaultLifetime = schema.Int(
title = u'Default Lifetime',
description = u'The default lifetime of entries',
@@ -74,7 +74,7 @@
def query(key, default=None, ns=None, raw=False):
"""query the cache for key in namespace, returns default if
not found. ns defaults to default namespace."""
-
+
def invalidate(key=None, ns=None, raw=False, dependencies=[]):
"""invalidates key in namespace which defaults to default
namespace, currently we can not invalidate just a namespace.
@@ -90,4 +90,38 @@
def keys(ns=None):
"""if trackKeys is True, returns the keys defined in the
namespace"""
-
+
+
+class IInvalidateCacheEvent(interface.Interface):
+ """An event which invalidates cache entries."""
+
+ cacheName = schema.TextLine(
+ title = u'cacheName',
+ description = u"""
+ Invalidate in the cache with this name.
+ If no name is given all caches are invalidated.
+ """,
+ required = False,
+ )
+
+ key = schema.TextLine(
+ title = u'key',
+ required = False,
+ )
+
+ ns = schema.TextLine(
+ title = u'namespace',
+ required = False,
+ )
+
+ raw = schema.Bool(
+ title = u'raw',
+ required = False,
+ default = False,
+ )
+
+ dependencies = schema.List(
+ title = u'Dependencies',
+ required = False,
+ )
+
Added: lovely.memcached/trunk/src/lovely/memcached/testing/README.txt
===================================================================
--- lovely.memcached/trunk/src/lovely/memcached/testing/README.txt 2007-05-08 08:15:40 UTC (rev 75620)
+++ lovely.memcached/trunk/src/lovely/memcached/testing/README.txt 2007-05-08 11:03:48 UTC (rev 75621)
@@ -0,0 +1,48 @@
+=====================
+Memcached Test Client
+=====================
+
+For testing we provide a memcache implementation which doesn't need a running
+memcache daemon.
+
+ >>> from lovely.memcached.testing import TestMemcachedClient
+ >>> cache = TestMemcachedClient()
+ >>> cache.set('value', 'key')
+ '613fb124907164bf8f0b04beb02cf59e'
+ >>> cache.query('key')
+ 'value'
+ >>> cache.invalidate(key='key')
+ >>> cache.query('key') is None
+ True
+
+Also the lifetime is handled.
+
+ >>> cache.set('value', 'key', 1)
+ '613fb124907164bf8f0b04beb02cf59e'
+ >>> cache.query('key')
+ 'value'
+ >>> from time import sleep
+ >>> sleep(1)
+ >>> cache.query('key') is None
+ True
+
+The TestMemcachedClient does it's best to simulate the behaviour of the
+original memcached implementation.
+
+ >>> from lovely.memcached.testing.memcache import SimulatedMemcached
+ >>> client = SimulatedMemcached()
+ >>> client.set(u'\xfckey', 'value', 1)
+ Traceback (most recent call last):
+ ...
+ UnicodeEncodeError: 'ascii' codec can't encode character u'\xfc' in position 0: ordinal not in range(128)
+
+ >>> client.set('key', u'\xfcvalue', 1)
+ Traceback (most recent call last):
+ ...
+ UnicodeEncodeError: 'ascii' codec can't encode character u'\xfc' in position 0: ordinal not in range(128)
+
+ >>> client.get(u'\xfckey')
+ Traceback (most recent call last):
+ ...
+ UnicodeEncodeError: 'ascii' codec can't encode character u'\xfc' in position 0: ordinal not in range(128)
+
Property changes on: lovely.memcached/trunk/src/lovely/memcached/testing/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: lovely.memcached/trunk/src/lovely/memcached/testing/__init__.py
===================================================================
--- lovely.memcached/trunk/src/lovely/memcached/testing/__init__.py 2007-05-08 08:15:40 UTC (rev 75620)
+++ lovely.memcached/trunk/src/lovely/memcached/testing/__init__.py 2007-05-08 11:03:48 UTC (rev 75621)
@@ -0,0 +1,20 @@
+##############################################################################
+#
+# Copyright (c) 2007 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+from memcache import TestMemcachedClient
+
Property changes on: lovely.memcached/trunk/src/lovely/memcached/testing/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: lovely.memcached/trunk/src/lovely/memcached/testing/memcache.py
===================================================================
--- lovely.memcached/trunk/src/lovely/memcached/testing/memcache.py 2007-05-08 08:15:40 UTC (rev 75620)
+++ lovely.memcached/trunk/src/lovely/memcached/testing/memcache.py 2007-05-08 11:03:48 UTC (rev 75621)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# Copyright (c) 2007 Lovely Systems and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+from datetime import datetime, timedelta
+
+from lovely.memcached.utility import MemcachedClient
+
+
+class TestMemcachedClient(MemcachedClient):
+ """A memcache client which doesn't need a running memcache daemon"""
+
+ def _instantiateClient(self, debug):
+ return SimulatedMemcached()
+
+
+class SimulatedMemcached(object):
+
+ def __init__(self):
+ self.cache = {}
+
+ def getStats(self):
+ return []
+
+ def set(self, key, data, lifetime=0):
+ # raise an error if not a string
+ str(key)
+ str(data)
+ if lifetime:
+ lifetime = datetime.now()+timedelta(seconds=lifetime)
+ else:
+ lifetime = None
+ self.cache[key] = (data, lifetime)
+ return True
+
+ def get(self, key):
+ str(key)
+ data = self.cache.get(key, None)
+ if data is None:
+ return None
+ if data[1] is None or datetime.now()<data[1]:
+ return data[0]
+ del self.cache[key]
+ return None
+
+ def delete(self, key):
+ if key in self.cache:
+ del self.cache[key]
+
+ def flush_all(self):
+ self.cache = {}
+
Property changes on: lovely.memcached/trunk/src/lovely/memcached/testing/memcache.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: lovely.memcached/trunk/src/lovely/memcached/tests.py
===================================================================
--- lovely.memcached/trunk/src/lovely/memcached/tests.py 2007-05-08 08:15:40 UTC (rev 75620)
+++ lovely.memcached/trunk/src/lovely/memcached/tests.py 2007-05-08 11:03:48 UTC (rev 75621)
@@ -22,15 +22,19 @@
def test_suite():
level1Suites = (
+ DocFileSuite(
+ 'testing/README.txt',
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
DocTestSuite(
- 'lovely.memcached.utility',
- optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ 'lovely.memcached.utility',
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
)
level2Suites = (
DocFileSuite(
- 'README.txt',
- optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ 'README.txt',
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
)
for suite in level2Suites:
Modified: lovely.memcached/trunk/src/lovely/memcached/utility.py
===================================================================
--- lovely.memcached/trunk/src/lovely/memcached/utility.py 2007-05-08 08:15:40 UTC (rev 75620)
+++ lovely.memcached/trunk/src/lovely/memcached/utility.py 2007-05-08 11:03:48 UTC (rev 75621)
@@ -124,8 +124,8 @@
for dep in dependencies:
depKey = self._buildDepKey(dep, ns)
keys = self.client.get(depKey)
- self.invalidate(depKey)
if keys is not None:
+ self.invalidate(depKey)
for key in keys:
self.client.delete(key)
@@ -203,7 +203,7 @@
self.storage.servers = self.servers
client = getattr(self.storage, 'client', None)
if client is None:
- client = memcache.Client(self.servers, debug=0)
+ client = self._instantiateClient(debug=0)
self.storage.client = client
return client
@@ -217,6 +217,8 @@
self._keysInit(self._v_storage)
return self._v_storage
+ def _instantiateClient(self, debug):
+ return memcache.Client(self.servers, debug=debug)
def _keysInit(self, storage):
storage.keys = {}
@@ -228,7 +230,6 @@
clients.add(storage.uid)
self.set(clients, 'clients', lifetime=0, ns=NS)
-
def _keysSet(self, key, ns, lifetime):
"""track a key"""
if not self.trackKeys or ns in (NS, STAMP_NS): return
More information about the Checkins
mailing list