[ZODB-Dev] ZODB cache reimplementation

Jim Fulton jim@zope.com
Thu, 24 Jan 2002 18:07:04 -0500


This is a multi-part message in MIME format.
--------------AB539BA6C42F05E6E3AFC9A5
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Toby Dickenson wrote:
> 
> I mentioned several weeks ago that I was planning to try changing
> cPickleCache to use a LRU policy in deciding which objects to
> ghostify, and stricter limits on the total size of the cache....

I'm glad you brought this up. I've been meaning to get back
with you on this. :)

> Reviewing cPickleCache before starting work on this I have encountered
> an aspect of its implementation that I didnt previously know about:
> Normally cPickleCache is used to store instances of persistent
> classes. However, cPickleCache contains some code to support the value
> being a class itself (rather than a class instance).
> 
> Whats that all about?

It's about supporting persistent classes.

> Is that capability used by anyone?

Absolutely. It's used by ZClasses. Note that we will
have more freedom,  with Python 2.2 to use them in a more 
uniform way, so we may be able to get rid of the distinction 
in the cache code.

Have you looked at the experiment I did in the Zope 3 branch
using weak references?  There are a number of things I
think we should do:

- Use weak references so that removal from the cache
  is automatic. Use a customn weak reference implementation
  to reduce memory consumption

- Use your idea of a doubly linked list, with recently
  used objects at one end.

- Separate the active objects from the ghosts, since we no longer, 
  with weak references, need to scan the ghosts.

I've worked up a UML class diagram that illustrates the idea. It's attached.

I imagine that this is close to what you've been thinking of. 
The cache has a dictionary that contains references to intermediate reference
objects. The reference objects have references to the cache and to persistent objects.
A persistent object is obtained from the cache through the cache reference.

A reference object will be in one of two linked lists in the cache, one for ghosts
and one for active objects.  The reference moves between lists when the persistent 
object changes states. List counts are also maintained for use by the cache
management software. When an object is accessed, it is moved to back of the
active list. When the cache does GC, it scans the active list from the front for objects
to be removed.  Normally, any object it finds may be removed, unless is is locked.
An object is locked when it is accessed from C. Perhaps there should be a separate
locked list. 

The cache reference is essentially a weak reference to the object. It does
not increase the persistent object's reference count. When a persistent object
is deallocated, it (in it's dealloc):

  - Removes (zeros) it's reference from the reference object,

  - Removes the reference object from whatever list it's in,

  - Removes itself from the cache's data mapping by deleting the
    key with the objects object id.

It's awfully tempting to combine the reference object with the persistent
object to avoid the malloc and Python object overhead and the object pointer. 
There's some  risk that somehow the deletion of the object from the cache's 
data mapping could fail on deallocation, which would lead to a memory error
of some kind later.

Thoughts?

I think it might be a good idea to discuss this verbally. Is there a good
time for me to call you?

Are you going to Python 10 by any chance?

Jim

--
Jim Fulton           mailto:jim@zope.com       Python Powered!        
CTO                  (888) 344-4332            http://www.python.org  
Zope Corporation     http://www.zope.com       http://www.zope.org
--------------AB539BA6C42F05E6E3AFC9A5
Content-Type: image/png;
 name="PersistentCache.png"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
 filename="PersistentCache.png"

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
m8Co/gVG+t4AAAAASUVORK5CYII=
--------------AB539BA6C42F05E6E3AFC9A5--