[ZODB-Dev] Support for graceful ZODB Class renaming

Pieter Nagel pieter@nagel.co.za
Sat, 18 Jan 2003 16:26:16 +0200


I believe that database schema changes (whether class renamings or
instance attribute rearrangements), should be done atomically in one fell
swoop.

There I favour solution 2 (a utility that transforms fixes all instances
of the renamed classes in the database), and I dislike the lazy-renaming
approach. I dislike the alias approach (supporting old class names
indefinitely) even more.

The way I do it is basically:

class Migrator:
    def __init__(self):
        self.oldToNewClassMapping = {}

    def renamedClass(self, oldName, newClass):
        parts = oldName.split('.')
        moduleName = '.'.join(parts[:-1])
        className = parts[-1]

        self.oldToNewClassMapping[(moduleName, className)] = newClass.__module__, newClass.__name__

    def migrateAll(self, conn):
        self._oldClassFactory = conn._db._classFactory
        conn._db.setClassFactory(self._classFactory)

        import OidSweep
        for oid in OidSweep.allOids(conn):
            ob = conn[oid]
            ob._p_activate()

    def _classFactory(self, connection, location, name, **kwargs):
        location, name = self.oldToNewClassMapping.get((location, name), (location, name))
        try:
            return self._oldClassFactory(connection, location, name, **kwargs)
        except:
            print 'classFactory failed on', location, name
            return PlaceHolderForDeletedClass

You instantiate a Migrator object and tell it about class renamings via
renamedClass (mine can also handle instance renamings and other ad-hoc
data transformations). You invoke migrateAll on a connection when ready.
The OidSweep.allOids functions returns all oids in the database (I
implemented it lazily in terms of a FileStorage's index, but one could
achieve the same with a iterator-generator and referencesf).

The only catch is this depends on setClassFactory which disappeared from
the latest ZODB :-)


On Thu, Jan 16, 2003 at 03:14:25PM -0500, Jim Fulton wrote:
>      A bother with this approach is that the aliases need to be maintained
>      as long as the old pickles exist in the database, which could be
>      indefinitely.

Yuck.

>      A real problem with this approach is that we could end up
>      unpickling objects with the wrong class if the old names get
>      reused by new classes.
...
>      This requires enough bad luck, however, that
>      we haven't been bitten by it yet AFAIK,

I think this could easily happen, especialy in iterative development
styles like XP where one tends to change the code a lot.

The problem domain has a concept - Portfolio, say - and one codes a
Portfolio class. Later one realises that this object actually only
embodies one's holdings in a fund, and renames it to FundAccount. But the
original Portfolio concept is still hanging in the air; at a future point
of development one finally embodies it in code - whammo! reusing the old
name.

-- 
     ,_
     /_)              /| /
    /   i e t e r    / |/ a g e l
    http://www.nagel.co.za