[ZODB-Dev] Bug in cPersistence.c? Transaction.register(object) not called

John D. Heintz jheintz@isogen.com
Tue, 8 May 2001 17:47:14 -0500


You didn't get two attachments?  Well, here are two scripts embeded in the 
email.

I think that the problem is something about the state of a Persistent object 
and interaction with .....

Oh, wait!  I know what the problem is!  In the code below I replace the 
global get_transaction() call with my own, but only after some Persistent 
objects have already cached the PyFunction pointer to the old one.  Doh!!!

Ok, here is the code anyway, but I know how to fix it.

John

#------------------START test.py-----------------------
import __main__
import sys
import thread
import threading
import time
import random
import traceback
from StringIO import StringIO

import ZODB
from Persistence import Persistent
from ZODB.Transaction import Transaction
from ZODB.FileStorage import FileStorage
from ZODB.POSException import ConflictError

from per_data import *

def tryCommitFunction(func, args):
    """
    Uses _tryFunction to apply the given function to the arguments
    and then performs a commit.
    Throws an appropriate exception upon failure.
    """
    sleepTime=.05
    for i in range(10):
        try:
            result = apply(func, args)
            t = get_transaction()
            print "Committing %s" % t._objects
            t.commit()
            return result
        except ConflictError:
            if i == 10 - 1:
                raise
            get_transaction().abort()
            time.sleep(sleepTime)
            sleepTime = sleepTime * 1.5

# ------------
# Global getCorbaSession() support
# Implements Thread Specific Storage pattern from POSA2
_threadToSessionMap = {}
def getCorbaSession():
    id = thread.get_ident()
    return _threadToSessionMap[id]
def registerCorbaSession(corbaSession):
    id = thread.get_ident()
    _threadToSessionMap[id] = corbaSession
def unregisterCorbaSession(force = 1):
    id = thread.get_ident()
    if force or _threadToSessionMap.has_key(id):
        del _threadToSessionMap[id]
        
__main__.__builtins__.getCorbaSession=getCorbaSession

# ------------
# Setup global get_transaction() to use getThorSession().get_transaction()
_threadToTransMap = {}
orig_get_transaction = __main__.__builtins__.get_transaction
def get_transaction():
    try:
        return getCorbaSession().get_transaction()
    except KeyError:
        return orig_get_transaction()

def install_get_transaction():
    __main__.__builtins__.get_transaction=get_transaction
# ------------


class CorbaSession:
    def __init__(self, db):
        self.trans = Transaction(0)
        self.conn = db.open()
        registerCorbaSession(self)

    def get_transaction(self):
        return self.trans

    def get_connection(self):
        return self.conn


CONTAINER = 'Container'
storage = FileStorage('data.fs')
db = ZODB.DB(storage)
db.setPoolSize(10000)

#Setup db
connection = db.open()
if not connection.root().has_key(CONTAINER):
    fc = FooContainer()
    connection.root()[CONTAINER] = fc
    get_transaction().commit()
    fc.createFoo('b')
    get_transaction().commit()
#connection.close()

install_get_transaction()

class TestThread(threading.Thread):
    def __init__(self, db):
        threading.Thread.__init__(self)
        print "Starting %s" % self
        self.db = db
        self.conn = db.open()

    def run(self):
        self.session = CorbaSession(self.db)
        for x in range(1):
            tryCommitFunction(self._run, ())
        #TestThread(self.db).start()

    def _run(self):
        print "*** get container"
        fooContainer = self.conn.root()[CONTAINER]
        print "*** create Foo"
        foo = tryCommitFunction(fooContainer.createFoo,
                                ('a',))
        print "created %s" % foo
        print "*** get all foos"
        foos = tryCommitFunction(fooContainer.getAllFoos, ())

        assert foo in foos, "%s not in %s" % (str(foo), str(foos))
        print "*** len(foos)=%s" % len(foos)
        for foo in foos:
            print "*** get bar"
            bar = tryCommitFunction(foo.getBar, ())
            print "*** get bar's name"
            tryCommitFunction(bar.getName, ())

for x in range(1):
    TestThread(db).start()
#--------------------END test.py---------------------------------
#--------------------START per_data.py----------------------------
import ZODB
from Persistence import Persistent

def enter():
    try:
        getCorbaSession().get_connection().sync()
    except:
        print "!!!!!!!"
    
class Foo(Persistent):
    def __init__(self):
        self.bar = None

    def setBar(self, bar):
        enter()
        self.bar = bar

    def getBar(self):
        enter()
        return self.bar

class Bar(Persistent):
    def __init__(self, name):
        self.name = name

    def getName(self):
        enter()
        return self.name

class FooContainer(Persistent):
    def __init__(self):
        self.list = []

    def __setstate__(self, state):
        Persistent.__setstate__(self, state)
        print "__setstate__ %s %s %s" % (self, dir(self), state)

    def __getstate__(self):
        result = Persistent.__getstate__(self)
        print "__getstate__ %s %s" % (self, result)
        return result

    def _p_deactivate(self):
        print "_p_deactivate %s %s" % (self, dir(self))
        Persistent._p_deactivate(self)

    def createBar(self, name):
        enter()
        bar = Bar(name)
        return bar

    def createFoo(self, barName):
        print "--- create Foo"
        print "--- len(self.list)=%s" % len(self.list)
        enter()
        print "--- init Foo"
        foo = Foo()
        print "---         %s" % foo
        bar = self.createBar(barName)
        foo.setBar(bar)
        self.list.append(foo)
        print "self._p_changed=%s" % self._p_changed
        self.list = self.list
        print "self._p_changed=%s" % self._p_changed
        #  XXX This isn't called and should be!!    
get_transaction().register(self)
        assert self in get_transaction()._objects
        print "--- len(self.list)=%s" % len(self.list)
        return foo

    def getAllFoos(self):
        enter()
        return self.list
#----------------------END per_data.py-------------------------



On Tuesday 08 May 2001 17:17, Jeremy Hylton wrote:
> >>>>> "JDH" == John D Heintz <jheintz@isogen.com> writes:
>
>   JDH> Attached is a test file and persistent data structures file
>   JDH> that demonstrates a problems we are having with using ZODB and
>   JDH> CORBA.
>
> Is the text after your message all one script?  Where is the data
> file?  I don't think I understand how to execute this code to produce
> the error.
>
> My other question is: What do you think the problem is?  It's hard to
> tell what has gone wrong by staring at the code <0.3 wink>.  An
> explanation of the apparent problem would be helpful.
>
> Jeremy