[ZODB-Dev] Persistent Mapping Classes

Chris McDonough chrism at plope.com
Fri Oct 21 11:30:43 EDT 2005


On Fri, 2005-10-21 at 03:45 -0600, Ron Wills wrote:
> On Thu, 2005-10-20 at 22:16 -0400, Tim Peters wrote:
> > [Ron Wills]
> > >  I hope this is the right place to ask about this...
> > 
> > Yup!  Although given the nearly detail-free scenarios you gave us, I hope
> > you weren't hoping for answers too ;-)
> > 
> 
> Ok, I'll simplify.
> 
> basically the MLDB is:
> 
> class MLDB:
>     def __init__(self, filename):
>         filestore = FileStorage(oodb)
>         database = DB(filestore)
>         conn = DB.open(database)
>         self.db = database
>         self.conn = conn
> 
>     def __del__(self):
>         self.db.close()


"oodb" is a global somewhere we don't know about?

FWIW, overriding __del__ has been a frequent source of problems for me
if I'm not certain that having __del__ called more than once is
idempotent.  I try to not do this, as a result, although it may be
perfectly fine in your application.

> This is a very thin wrapper, which only one instance is created globally

Does your application use multiple threads?

> and all other methods of the class are for our query engine and
> importing routines from a third party sql db. This is not the source of
> the problem and can be removed. So if we take the OORTable class as in
> the previous email (and there is no __del__ method) with a example as
> follows:
> 
> from persistent import Persistent
> from persistent.mapping import PersistentMapping
> 
> class OORTable(PersistentMapping):
>     def __init__(self, base_class):
>         PersistentMapping.__init__(self)
> 
>         self._table_name = name or base_class.__name__
>         self._columns = PersistentMapping()
>         self._record_count = 0
> 
>     del __len__(self):
>         return self._record_count
> 
>     # The other methods are trivial for the example

I hope so, because __init__ can't work as it's written (there doesn't
appear to be a "name" binding, so that bit of code should fail with a
NameError).

> class SomeClass(Persistent):
>     pass
> 
> filestore = FileStorage('myzodb.fs')
> db = DB(filestore)
> conn = DB.open(db)
> 
> conn.root['SomeClass'] = OORTable(SomeClass)
> an_obj = SomeClass()
> an_obj.an_attr = 'a value'
> conn.root['SomeClass']['an_obj'] = an_obj
> 
> db.close()
> 
> This all works as expected, until we reopen the database as I specified
> in the my previous email.

I would expect that, because you didn't use or commit a transaction.
Essentially, on a reopen of the database, you will be using the state
that existed in the database before you did all that (your changes will
just vanish).

> 
> filestore = FileStorage('myzodb.fs')
> db = DB(filestore)
> conn = DB.open(db)
> 
> ootable = conn.root['SomeClass']  # No problem
> an_obj = conn.root['SomeClass']['an_obj'] # No problem
> print an_obj.an_attr # No problem
> 
> len(ootable) # Most of the time raises an Attribute error that 
>              # _record_count doesn't exist. This also happens
>              # with other methods that accesses any other of
>              # the attributes defined.
> 
> db.close()

Can't really guess about the _record_count error, given that you didn't
commit a transaction and we can't know what the state of the database
was before you tried to change it.

> 
>   To hopefully to make this problem clearer any objects we put into
> 'myzodb.fs' that inherit Persistent, or a PersistentMapping(), or a
> OOBTree() end up serialized in 'myzodb.fs'. This one class that inherits
> PersistentMapping, OORTable, is inconstantly serializing its attributes
> to 'myzodb.fs'. Why this is happening or to give consistent testing for
> the failure is beyond me, and unfortunately I cannot freely share the
> code as this is a contracted commercial product :(. But once again,
> here's how it does fail:
> 
> Test Suite:
> 1. Open ZODB.fs & populate
> 2. run tests all pass
> 3. close ZODB.fs, no failures
> 4. reopen ZODB.fs
> 5. run the same tests, fails from missing attributes in OORTable
> 6. close ZODB.fs
> 
> Second Test Suite
> 1. Open ZODB.fs & populate
> 3. close ZODB.fs, no failures
> 4. reopen ZODB.fs
> 5. run tests all pass
> 6. close ZODB.fs, no failures
> 7. repeat from 4 with no problems
> 
> The same sort of bazaar behavior from the web site:
> 
> >>> from ClearWater import MLDB 
> >>> mldb = MLDB.MLDB(_cfg)
> >>> MLDB.mldb = mldb
> >>> # In this case none of the attribute for any OORTable records gets
> >>> # serialized to the ZODB. If shutdown and restarted then 
> >>> # Attribute errors are raised from all the OORTable objects
> 
> >>> from ClearWater import MLDB
> >>> MLDB.mldb = MLDB.MLDB(_cfg)
> >>> # In this case the attributes do get serialize and on a shutdown and
> >>> # restart all the OORTable's attribute exist with proper values.
> 
> Note that all access to the ZODB is done through the global MLDB.mldb
> instance and the minor difference in the above code is the difference
> from a complete or corrupted ZODB.

I don't believe there is any corruption here; if there is, there isn't
any evidence of that.  I do believe that you think the state of the
database isn't what you expect it to be when reopened, but the fact that
there aren't any "import transaction; transaction.commit()" calls or
variants in your code would explain that at large.

>   What is making this even more confusing is it's just the attributes
> from OORTable that are not serialized to the ZODB.fs. All the other
> objects are present, complete and valid. The only thing I'm seeing is
> that PersistentMapping is not consistently serializing it's attributes.
>   This is about the best consistent tests for this problem I can give
> after extensively debugging code and changing the test suites for 3
> days, to narrow down where this problem lies, just short of digging into
> the persistent modules C code.

Try committing the transaction.

- C




More information about the ZODB-Dev mailing list