Re: Migrating to BTrees 4.x

"David Glick (Glick Software)" <[email protected]>
Newsgroups gmane.comp.web.zope.zodb
Message-ID <[email protected]>
On 11/19/16 4:28 PM, Jason Madden wrote:
>> On Nov 19, 2016, at 17:58, Jim Fulton <[email protected]> wrote:
>>
>>
>>
>> On Sat, Nov 19, 2016 at 3:21 PM, David Glick <[email protected]> wrote:
>>> I'm working on updating Plone to support ZODB 4 and 5.
>>>
>>> So far most of the blockers are because BTrees 4.x no longer allows using keys whose ordering is not well-defined, including None, and there are various places where Plone was using None as a key. I understand the reason for the restriction and am making progress on making Plone no longer do this.
>>>
>> Hm. IMO, the restriction should be in adding items, not on unpickling.  We should go to great lengths, to avoid unpickling errors.
>>
>> I think we should avoid raising this error in __setstate__. It should be easy to fix.
> Interestingly, the Python implementation currently *doesn't* raise an error in this case, at least in small trees.
>
> I pickled an OOBTree from ZODB3 3.10.7 that looked like {None: 42}.
>
> When I use the pure Python implementation, it loads:
>
>    In [9]: import pickle
>
>    In [10]: pickle.loads('ccopy_reg\n__newobj__\np0\n(cBTrees.OOBTree\nOOBTree\np1\ntp2\nRp3\n((((NI42\ntp4\ntp5\ntp6\ntp7\nb.')
>    Out[10]: <BTrees.OOBTree.OOBTree at 0x10849eed0>
>
>    In [11]: bt2 = pickle.loads('ccopy_reg\n__newobj__\np0\n(cBTrees.OOBTree\nOOBTree\np1\ntp2\nRp3\n((((NI42\ntp4\ntp5\ntp6\ntp7\nb.')
>
>    In [12]: bt2
>    Out[12]: <BTrees.OOBTree.OOBTree at 0x10863fa50>
>
>    In [13]: type(bt2)
>    Out[13]: BTrees.OOBTree.OOBTreePy
>    
>    In [14]: bt2[None]
>    Out[14]: 42
>
>    In [15]: None in bt2
>    Out[15]: True
>
> I can't set None to a new value:
>
>    In [17]: bt2[None] = 43
>    ---------------------------------------------------------------------------
>    TypeError                                 Traceback (most recent call last)
>
> But I can delete it:
>
>    In [30]: len(bt2)
>    Out[30]: 1
>
>    In [31]: del bt2[None]
>
>    In [32]: len(bt2)
>    Out[32]: 0
>
>
> Iterating keys and values appear to work as expected, too.
>
> Are these the semantics you would expect? If there are unorderable values in the tree, is that going to cause corruption on adding new keys or removing other keys?
>
> Some such keys may not be removable, at least in extreme cases.
>
> Here I am back in ZODB3 again:
>
>    In [6]: class Bad(object):
>     ...:     def __eq__(self, other):
>     ...:         return False # Corner case extreme example of broken object
>     ...:
>
>
>    In [8]: bt[Bad()] = 42
>
> And here's loading that pickle in BTrees 4 Python. Note that I can load it successfully, but I can't delete it, even though I'm using the identical instance:
>
>    In [35]: bt2 = pickle.loads('ccopy_reg\n__newobj__\np0\n(cBTrees.OOBTree\nOOBTree\np1\ntp2\nRp3\n((((ccopy_reg\n_reconstructor\np4\n(c__main__\nBad\np5\nc__builtin__\nobject\
>      ...: np6\nNtp7\nRp8\nI42\ntp9\ntp10\ntp11\ntp12\nb.')
>
>    In [36]: bt2
>    Out[36]: <BTrees.OOBTree.OOBTree at 0x1087d72d0>
>
>    In [37]: len(bt2)
>    Out[37]: 1
>
>    In [39]: list(bt2.keys())
>    Out[39]: [<__main__.Bad at 0x108964b90>]
>
>    In [40]: bad = list(bt2.keys())[0]
>
>    In [41]: bad in bt2
>    Out[41]: False
>
>    In [42]: del bt2[bad]
>    ---------------------------------------------------------------------------
>    KeyError                                  Traceback (most recent call last)
>
> Hmm, then I tried that same thing again with the C implementation, and it behaves very differently (correctly?):
>
>    In [2]: class Bad(object):
>       ...:     def __eq__(self, other): return False
>       ...:
>
>
>    In [4]: bt = pickle.loads('ccopy_reg\n__newobj__\np0\n(cBTrees.OOBTree\nOOBTree\np1\ntp2\nRp3\n((((ccopy_reg\n_reconstructor\np4\n(c__main__\nBad\np5\nc__builtin__\nobject\np
>     ...: 6\nNtp7\nRp8\nI42\ntp9\ntp10\ntp11\ntp12\nb.')
>
>    In [5]: bt
>    Out[5]: <BTrees.OOBTree.OOBTree at 0x1053d3e60>
>
>    In [6]: list(bt.keys())
>    Out[6]: [<__main__.Bad at 0x1055fc390>]
>
>    In [7]: bad = list(bt.keys())[0]
>
>    In [9]: bad in bt
>    Out[9]: True
>
>    In [10]: del bt[bad]
>
> But wait! Checking even further, I find that even in BTrees 4, I can still insert Bad into instances of the C implementation, but I can't into the Python implementation:
>
>    In [10]: bt = BTrees.OOBTree.OOBTree()
>
>    In [11]: bt[Bad()] = 42
>
>    In [14]: bt2 = BTrees.OOBTree.OOBTreePy()
>
>    In [15]: bt2[Bad()] = 42
>    ---------------------------------------------------------------------------
>    TypeError                                 Traceback (most recent call last)
>
>    TypeError: Can't use default __cmp__
>
> Maybe this is an extreme and useless example of a broken object when it comes to ordering and testing what happens if we allow such objects into a BTree on unpickling. But it seems safe to say there are at least some discrepancies and some clear bugs in the handling of unorderable objects still.

Is the above with BTrees master or a release? I just noticed that you 
did some work on the consistency of the two implementations in August, 
which hasn't been released.

-- 
You received this message because you are subscribed to the Google Groups "zodb" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
For more options, visit https://groups.google.com/d/optout.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.