On Aug 2, 2006, at 6:03 AM, Dominik Huber wrote:
Hi Gary
Thanks for your inputs. I was also offline this days...
Gary Poster wrote:
[...]
Well, first of all, I suspect your situation, based on what I've
seen so far, looks something like this:
- code creates obj P1
- code puts P1 in intids, with connection.
- code does something incorrect (either your code or code that you
are relying on), probably involving a savepoint because of your
discussion below, that keeps keyreference to P1 in intids BTree,
but rolls back addition of P1 to connection. Right now we have
the bad data (a key reference to an object without a connection in
the BTree), but we don't know about it yet.
- Now code creates obj P2.
- code tries to put P2 in intids. If the BTree logic happens to
make it compare against the bad keyreference to P1, you get the
__cmp__ error. This is a red herring for debugging: the problem
has already occurred (see above).
Yup.
Few more details:
There is a import handler which creates p1 , p2 to pn and sets
those object to a certain folder. After each addition of a certain
p an initializer subscriber listen to IObjectAddedEvent is invoked
too. The error occurs sometimes during this initialization. In case
of an error the creation and initialization of the error-prone p is
rolled back by an Importer (compare the following code):
class Importer(object):
def __call__(self, importhandelr)
[...]
for d in import_reader:
if not d[first].startswith('#'):
savepoint = transaction.savepoint()
try:
data = dict([(key, value for key, value in
d.items()])
importhandler(data) # <- call importhandler
which creates the p
transaction.commit()
except Exception, e:
savepoint.rollback() # clean up
self.log.error(e)
Huh. I wonder if a savepoint rollback cleans up sufficiently after a
commit; I've never used it like that. Out of curiosity, if you take
the commit out does the problem go away? Generally, looking at the
code, one of the very first things that Transaction.commit does is
_invalidate_all_savepoints, so the pattern you have here doesn't look
advisable.
That said, if that were the problem here, it looks like you should
have gotten an exception when you did the rollback, so it's probably
just something to change, but not the cause of what we're talking about.
Also, in the vein of things that are probably not the cause of the
problem we are discussing, but are maybe worth highlighting, the
whole bare except thing around a commit is an interesting
discussion. For a one-time import, maybe this is fine, but for more
robust code I prefer something a little more sophisticated. I've
tried various things over the years; my current recipe is something
like this:
try:
...do stuff and commit...
except ZODB.POSException.TransactionError:
...abort and retry some number of times, and eventually raise...
except (SystemExit, KeyboardInterrupt, ZODB.POSException.POSError):
...abort and raise...
except:
...abort and log, or whatever is appropriate...
That's probably somewhat controversial--maybe TransactionError is too
broad, for instance--but I offer it for what it's worth.
I'm pretty sure your BTree is already bad by the time you get to
the failing __cmp__.
Yup
The only way I know to fix it at this late stage in the story is
drastic: you'll need to create a new BTree, iterate over the old
one and add all the contents except for the broken keyreferences,
and replace the old intid BTree with the new one.
I don't think that's appropriate error catching for the BTree
code. I think if you get the error you describe, you have a
serious problem in "your" code (i.e., code that you've written or
code that you're relying on in your stack, like Zope). How--and
when--to handle it is application policy, not something that
should be in the base intids, I think.
Yes, but the whole problem seems not so heavy:
1. If I restart the server no wrong intid entries are there.
One possible explanation is that you have some in-memory data
structure that the savepoint can't roll back. Another is that the
savepoint code isn't quite right. A third is that the BTree code
isn't quite right. I suspect something like the first more than the
second or third, but that doesn't rule anything out. Have you
analyzed what exception triggers the problem? Have you analyzed the
bad in-memory tree to see if it gives you any clues?
2. If I patch the __cmp__ method of KeyReferenceToPersistent the
proposed way it only runs right after a wrong p creation into the
except. After another successful transaction commit I don't have
any bad intids entries
IMO that points that somehow the persistent data are ok. That we
only migh