[TransWarp] storage: order of actions on commit

Phillip J. Eby pje at telecommunity.com
Tue Feb 18 17:29:31 EST 2003


At 12:24 PM 2/18/03 +0100, Ulrich Eck wrote:
>creating/retrieving folders works nice and i use the way you described for 
>creating oid's:
>
>    def _new(self, ob):
>        if ob.parent is None:
>            folder = ob.name
>        else:
>            folder = '%s.%s' % (self.oidFor(ob.parent)[0], ob.name)
>        folder = naming.CompositeName.parse(folder, imapFolderPath)

If you're doing what I think you're doing there, then:

     folder = imapFolderPath(folder)

would suffice.  The only reason to use CompositeName.parse is if you expect 
there to be another '/'-separated namespace in the name, and that won't be 
the case unless the object's name contains a '/' (presumably an invalid 
character).

I would also note, by the way, that there's little point to parsing the 
name and then making it also a tuple to serve as an oid.  Simply taking the 
unparsed folder name string as an oid would probably suffice, unless you 
have needs I don't know about.  I don't recommend mixing folders and 
message retrieval in the same DM, because it's just going to clutter a lot 
of your methods with conditional code.



>        ob._p_oid = (folder, )
>
>        print '_new', folder
>        conn = self.connection
>        status, body = conn.create(str(folder))

Notice here that you're now unparsing the previously parsed folder 
name...  Keeping it a string in the first place would of course be easier...


>        if not status == 'OK':
>            raise Exception, 'Cannot create Folder: %s' % folder
>
>        return ob._p_oid
>
>
>
>so the next questions are:
>
>- How should deleting objects be implemented ??
>   i haven't found anything in the code/interfaces that says something about
>   how to implement it. probably the Element is responsible to delete itself
>   .. but are there any methods that should be implemented in the dm to 
> support that ?

Deletion needs to be handled by a DM method, not an Element 
method.  Elements don't know how they're stored, so they can't delete 
themselves.  If you have a composite relationship where an object can't 
exist once it's removed from its container, then the _save() method should 
treat that as a deletion, and remove the deleted object from the DM's 
cache.  For other types of deletion, it's an application-level operation, 
and so is directed to the DM.

Perhaps I should add a 'delItem()' method to EntityDM, but of course it 
wouldn't do anything, because that's what you would need to fill in.  So 
I'm sort of +0 on it for now.


>- Is there any way to determine which values of an element have been 
>changed ??
>   or is it intended to just save the complete state of an object on _save() ?

You can always call the DM's _load() method to reload its original state 
and compare, or you can stuff a memento of your choosing into the state at 
_load() time.  A simple way to do that is to stick the uninterpreted 
database rows or whatever into a special attribute.

But I think that in most cases, doing that kind of tracking is more work 
than it's worth; certainly I'd consider it a premature optimization if you 
aren't sure you need it.

I can think of one place where I might do it, though...  LDAP group 
membership.  Large LDAP groups are slow to load over-the-wire, so I'd use 
QueryLink objects for their membership fields, and a LazyLoader (see 
peak.storage.lazy_loader) on the LDAP user objects corresponding field for 
what groups they belong to.  The LazyLoader would actually load a list by 
querying for all the groups that the user was a member of, and it would 
also hide a memento of the groups' DNs in the user object.  Then, the 
_save() operation for the user object would actually be the thing that 
added or removed the user from a particular group, rather than a _save() 
operation on the group.  This would mean I'd never have to load a group's 
entire membership just to add or remove a user; I could just send 
modifications to the LDAP server.

The way you use a LazyLoader is to subclass it and add the behavior you 
want, then put an instance of it into the object's state at _load() 
time.  If the attribute is accessed via a Feature, the LazyLoader's 
'_load()' method will be called, and it will have the opportunity to 
replace itself in the object's state.

Your _save() method will need to check before saving such an attribute, to 
ensure that it's not the LazyLoader itself.  If it is a LazyLoader 
instance, then you will know the value has not been changed or even accessed.




More information about the PEAK mailing list