Commit 7bb6c63c authored by Jim Fulton's avatar Jim Fulton

Changed the dropped-cache event to a StaleCache event that's published

whenever the cache is stale. This is generated before dropping a cache
and starting cold so that an application can take more drastic
measures. The stale-cache event also now has a storage attribute with
the storage that has a stale cache.
parent c8e3741d
...@@ -1254,6 +1254,8 @@ class ClientStorage(object): ...@@ -1254,6 +1254,8 @@ class ClientStorage(object):
elif ltid and ltid != utils.z64: elif ltid and ltid != utils.z64:
self._cache.setLastTid(ltid) self._cache.setLastTid(ltid)
zope.event.notify(ZEO.interfaces.StaleCache(self))
# From this point on, we do not have complete information about # From this point on, we do not have complete information about
# the missed transactions. The reason is that cache # the missed transactions. The reason is that cache
# verification only checks objects in the client cache and # verification only checks objects in the client cache and
...@@ -1265,7 +1267,6 @@ class ClientStorage(object): ...@@ -1265,7 +1267,6 @@ class ClientStorage(object):
if self._cache and self._drop_cache_rather_verify: if self._cache and self._drop_cache_rather_verify:
logger.critical("%s dropping stale cache", self.__name__) logger.critical("%s dropping stale cache", self.__name__)
zope.event.notify(ZEO.interfaces.CacheDroppedEvent())
self._cache.clear() self._cache.clear()
if ltid: if ltid:
self._cache.setLastTid(ltid) self._cache.setLastTid(ltid)
......
...@@ -14,10 +14,13 @@ ...@@ -14,10 +14,13 @@
import zope.interface import zope.interface
class CacheDroppedEvent(object): class StaleCache(object):
"""A ZEO Cache file was dropped to avoid verification """A ZEO cache is stale and requires verification.
""" """
def __init__(self, storage):
self.storage = storage
class IServeable(zope.interface.Interface): class IServeable(zope.interface.Interface):
"""Interface provided by storages that can be served by ZEO """Interface provided by storages that can be served by ZEO
""" """
......
...@@ -6,9 +6,13 @@ files. If a client has beed disconnected for too long, cache verification ...@@ -6,9 +6,13 @@ files. If a client has beed disconnected for too long, cache verification
might be necessary, but cache verification can be very hard on the might be necessary, but cache verification can be very hard on the
storage server. storage server.
When verification is needed, a ZEO.interfaces.StaleCache event is
published. Applications may handle this event to perform actions such
as exiting the process to avoid a cold restart.
ClientStorage provides an option to drop it's cache rather than doing ClientStorage provides an option to drop it's cache rather than doing
verification. When this option is used, and verification would be verification. When this option is used, and verification would be
necessary, ClientStorage: necessary, after publishing the event, ClientStorage:
- Invalidates all object caches - Invalidates all object caches
...@@ -17,8 +21,6 @@ necessary, ClientStorage: ...@@ -17,8 +21,6 @@ necessary, ClientStorage:
- Logs a CRITICAL message. - Logs a CRITICAL message.
- Publishes a ZEO.interfaces.CacheDroppedEvent event.
Here's an example that shows that this is actually what happens. Here's an example that shows that this is actually what happens.
Start a server, create a cient to it and commit some data Start a server, create a cient to it and commit some data
...@@ -58,7 +60,14 @@ logging and event data: ...@@ -58,7 +60,14 @@ logging and event data:
>>> handler = zope.testing.loggingsupport.InstalledHandler( >>> handler = zope.testing.loggingsupport.InstalledHandler(
... 'ZEO.ClientStorage', level=logging.ERROR) ... 'ZEO.ClientStorage', level=logging.ERROR)
>>> events = [] >>> events = []
>>> zope.event.subscribers.append(events.append) >>> def event_handler(e):
... events.append((
... len(e.storage._cache), str(handler), e.__class__.__name__))
>>> zope.event.subscribers.append(event_handler)
Note that the event handler is saving away the length of the cache and
the state of the log handler. We'll use this to show that the event
is generated before the cache is dropped or the message is logged.
Now, we'll restart the server on the original address: Now, we'll restart the server on the original address:
...@@ -68,6 +77,19 @@ Now, we'll restart the server on the original address: ...@@ -68,6 +77,19 @@ Now, we'll restart the server on the original address:
Now, let's verify our assertions above: Now, let's verify our assertions above:
- Publishes a stale-cache event.
>>> for e in events:
... print e
(3, '', 'StaleCache')
Note that the length of the cache when the event handler was
called waa non-zero. This is because the cache wasn't cleared
yet. Similarly, the dropping-cache message hasn't been logged
yet.
>>> del events[:]
- Drops or clears it's client cache. (The end result is that the cache - Drops or clears it's client cache. (The end result is that the cache
is working but empty.) is working but empty.)
...@@ -87,14 +109,6 @@ Now, let's verify our assertions above: ...@@ -87,14 +109,6 @@ Now, let's verify our assertions above:
>>> handler.clear() >>> handler.clear()
- Publishes a cache-dropped event.
>>> for e in events:
... print e.__class__.__name__
CacheDroppedEvent
>>> del events[:]
If we access the root object, it'll be loaded from the server: If we access the root object, it'll be loaded from the server:
>>> conn.root()[1].x >>> conn.root()[1].x
...@@ -130,6 +144,14 @@ another client: ...@@ -130,6 +144,14 @@ another client:
(When a database is created, it checks to make sure the root object is (When a database is created, it checks to make sure the root object is
in the database, which is why we get 1, rather than 0 objects in the cache.) in the database, which is why we get 1, rather than 0 objects in the cache.)
- Publishes a stake-cache event.
>>> for e in events:
... print e
(2, '', 'StaleCache')
>>> del events[:]
- Logs a CRITICAL message. - Logs a CRITICAL message.
>>> print handler >>> print handler
...@@ -138,16 +160,57 @@ in the database, which is why we get 1, rather than 0 objects in the cache.) ...@@ -138,16 +160,57 @@ in the database, which is why we get 1, rather than 0 objects in the cache.)
>>> handler.clear() >>> handler.clear()
- Publishes a cache-dropped event. If we access the root object, it'll be loaded from the server:
>>> for e in events: >>> conn = db.open()
... print e.__class__.__name__ >>> conn.root()[1].x
CacheDroppedEvent 11
If we access the root object, it'll be loaded from the server:
Finally, let's look at what happens without the
drop_cache_rather_verify option:
>>> db.close()
>>> db = ZEO.DB(addr, client='cache')
>>> wait_connected(db.storage)
>>> conn = db.open() >>> conn = db.open()
>>> conn.root()[1].x >>> conn.root()[1].x
11 11
>>> conn.root()[2] = conn.root().__class__()
>>> transaction.commit()
>>> len(db.storage._cache)
4
>>> stop_server(admin)
>>> addr2, admin = start_server(keep=1)
>>> db2 = ZEO.DB(addr2)
>>> wait_connected(db2.storage)
>>> conn2 = db2.open()
>>> for i in range(5):
... conn2.root()[1].x += 1
... transaction.commit()
>>> db2.close()
>>> stop_server(admin)
>>> _, admin = start_server(zeo_conf=dict(invalidation_queue_size=1),
... addr=addr)
>>> wait_connected(db.storage)
>>> for e in events:
... print e
(4, '', 'StaleCache')
>>> print handler
<BLANKLINE>
>>> len(db.storage._cache)
3
Here we see the cache wasn't dropped, although one of the records was
invalidated during verification.
.. Cleanup
>>> db.close() >>> db.close()
>>> handler.uninstall()
>>> zope.event.subscribers.remove(event_handler)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment