[Zodb-checkins] CVS: Zope3/lib/python/Persistence - Cache.py:1.1.2.1 ICache.py:1.1.4.1
Jim Fulton
jim@zope.com
Sun, 25 Nov 2001 14:49:31 -0500
Update of /cvs-repository/Zope3/lib/python/Persistence
In directory cvs.zope.org:/tmp/cvs-serv21091
Added Files:
Tag: Zope-3x-branch
Cache.py ICache.py
Log Message:
Initial new cache implementation with tests.
=== Added File Zope3/lib/python/Persistence/Cache.py ===
##############################################################################
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 1.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.
##############################################################################
from time import time
from sys import getrefcount
from weakref import ref
class Cache(object):
__iter=None
def __init__(self, size=500, inactive=300):
self.__ghosts={}
self.__gget=self.__ghosts.get
self.__active={}
self.__aget=self.__active.get
self.__size=size
self.__inactive=300
def __getitem__(self, oid):
o = self.__gget(oid, self)
if o is self:
o = self.__active[oid]
o=o()
if o is None:
raise KeyError, oid
else:
return o
def get(self, oid, default=None):
o = self.__gget(oid, self)
if o is self:
o = self.__active.get(oid, self)
if o is self: return default
o=o()
if o is None:
return default
else:
return o
def __setitem__(self, oid, object):
if object._p_changed is None:
# ghost
self.__ghosts[oid] = ref(object, _dictdel(oid, self.__ghosts))
else:
self.__active[oid] = ref(object, _dictdel(oid, self.__active))
def __len__(self):
return len(self.__ghosts)+len(self.__active)
def setstate(self, oid, object):
try: del self.__ghosts[oid]
except KeyError: pass
self.__active[oid] = ref(object, _dictdel(oid, self.__active))
def incrgc(self, multiple=1):
na=len(self.__active)
if na < 1: return
# how many objects do we scan?
n=min(multiple * max((na-self.__size)/10, 3), na)
# how long can objects be inactive?
inactive = self.__inactive * (
0.2 + 0.1 * (min(100, 8 * self.__size/na))
)
active=self.__active
aget=active.get
ghosts=self.__ghosts
doomed=[]
now=int(time()%86400)
i=self.__iter
if i is None:
i=iter(self.__active)
while n:
n-=1
try: oid = i.next()
except StopIteration:
del self.__iter
return
ob=aget(oid, self)
if ob is self: continue
ob=ob()
state = ob._p_changed
if state==0 and abs(ob._p_atime-now) > inactive:
doomed.append(oid)
continue
if state is None:
doomed.append(oid)
for oid in doomed:
ob=aget(oid, self)
if ob is self: continue
ob=ob()
ob._p_deactivate()
state = ob._p_changed
if state is None:
del active[oid]
ghosts[oid] = ref(ob, _dictdel(oid, ghosts))
def full_sweep(self):
now=int(time()%86400)
active=self.__active
ghosts=self.__ghosts
na=len(active)
# how long can objects be inactive?
inactive = self.__inactive * (
0.2 + 0.1 * (min(100, 8 * self.__size/na))
)
doomed=[]
for oid in active:
ob=active[oid]
ob=ob()
state = ob._p_changed
if state==0 and abs(ob._p_atime-now) > inactive:
doomed.append(oid)
continue
if state is None:
doomed.append(oid)
for oid in doomed:
ob._p_deactivate()
state = ob._p_changed
if state is None:
del active[oid]
ghosts[oid] = ref(ob, _dictdel(oid, ghosts))
def minimize(self):
active=self.__active
aget=active.get
ghosts=self.__ghosts
# Grump: I cant use an iterator because the size will change
# during iteration. :(
for oid in active.keys():
ob=aget(oid, self)
if ob is self: continue
ob=ob()
ob._p_deactivate()
if ob._p_changed is None:
del active[oid]
ghosts[oid] = ref(ob, _dictdel(oid, ghosts))
self.__iter=None
def invalidate(self, oids):
if oids is None: oids=iter(self.__active)
aget=self.__aget
active=self.__active
ghosts=self.__ghosts
for oid in oids:
ob=aget(oid, self)
if ob is self: continue
ob=ob()
del ob._p_changed
del active[oid]
ghosts[oid] = ref(ob, _dictdel(oid, ghosts))
def statistics(self):
return {
'ghosts': len(self.__ghosts),
'active': len(self.__active),
}
class _dictdel(object):
__slots__='oid', 'dict'
def __init__(self, oid, dict): self.oid, self.dict = oid, dict
def __call__(self, *args):
del self.dict[self.oid]
=== Added File Zope3/lib/python/Persistence/ICache.py ===
##############################################################################
#
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# This license has been certified as Open Source(tm).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions in source code must retain the above copyright
# notice, this list of conditions, and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. Digital Creations requests that attribution be given to Zope
# in any manner possible. Zope includes a "Powered by Zope"
# button that is installed by default. While it is not a license
# violation to remove this button, it is requested that the
# attribution remain. A significant investment has been put
# into Zope, and this effort will continue if the Zope community
# continues to grow. This is one way to assure that growth.
#
# 4. All advertising materials and documentation mentioning
# features derived from or use of this software must display
# the following acknowledgement:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# In the event that the product being advertised includes an
# intact Zope distribution (with copyright and license included)
# then this clause is waived.
#
# 5. Names associated with Zope or Digital Creations must not be used to
# endorse or promote products derived from this software without
# prior written permission from Digital Creations.
#
# 6. Modified redistributions of any form whatsoever must retain
# the following acknowledgment:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# Intact (re-)distributions of any official Zope release do not
# require an external acknowledgement.
#
# 7. Modifications are encouraged but must be packaged separately as
# patches to official Zope releases. Distributions that do not
# clearly separate the patches from the original work must be clearly
# labeled as unofficial distributions. Modifications which do not
# carry the name Zope may be packaged in any form, as long as they
# conform to all of the clauses above.
#
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations. Specific
# attributions are listed in the accompanying credits file.
#
##############################################################################
# Hack to overcome absense of Interface package
try:
from Interface import Interface
except ImportError:
class Interface: pass
class ICache(Interface):
"""In-memory object cache
Cache objects are used by data managers to implement in-memory
object caches with automatic object deactivation and removal of
unreferenced objects.
Cache objects depend heavily on the Persistent object C API.
"""
def __getitem__(key):
"""Get a cached object
"""
def __setitem__(key, value):
"""Add an object to the cache
"""
def __len__():
"""Return the number of objects in the cache
"""
def get(oid, default=None):
"""Get a cached object
"""
def incrgc(multiple=1):
"""Perform incremental garbage collection
An positive integer argument can be provided to speify a
number of incremental steps to take.
"""
def full_sweep():
"""Perform a full sweep of the cache
"""
def minimize():
"""Remove as many objects as possible from the cache
"""
def invalidate(oids):
"""Invalidate the objects for the given sequence of object ids
If oids is None, all of the objets in the cache are
invalidated.
"""
class ICachePolicy(Interface):
def maximum_quiet(cache_size):
"""Return a number of seconds
Objects that haven't been accessed in the last number seconds
should be deactivated.
"""
def incremental_check_count(cache_size):
"""Return the number of objects that should be checked in an
incremental garbage collection.
"""