简体   繁体   English

CSharpTest.Net.Collections.BPlusTree最近缓存错误?

[英]CSharpTest.Net.Collections.BPlusTree RecentCache bug?

I've been using this implementation of B Plus Tree for some time. 我使用B Plus Tree的这种实现已有一段时间了。 I've noticed that the 'Recent' cache is buggy. 我注意到“最近”缓存存在错误。 Here's how the bug is produced: 错误产生的方式如下:

  1. I add some KVPs, and commit the tree. 我添加一些KVP,并提交树。
  2. I add some more KVPs and rollback the tree. 我添加了一些KVP并回滚了树。
  3. I add some more KVPs and commit the tree. 我再添加一些KVP并提交树。
  4. I restart my application and repeat step 1,2 and 3 我重新启动我的应用程序并重复步骤1,2和3

The tree throws an InvalidNodeHandleException when executing step 3 after the restart, which comes exactly 重新启动后执行步骤3时,树会抛出InvalidNodeHandleException异常

at CSharpTest.Net.Collections.BPlusTree`2.NodeCacheNormal.Lock(NodePin parent, LockType ltype, NodeHandle child)
at CSharpTest.Net.Collections.BPlusTree`2.RootLock..ctor(BPlusTree`2 tree, LockType type, Boolean exclusiveTreeAccess, String methodName)
at CSharpTest.Net.Collections.BPlusTree`2.LockRoot(LockType ltype, String methodName)

The following assertion fails. 以下断言失败。

InvalidNodeHandleException.Assert(Storage.TryGetNode(child.StoreHandle, out node, NodeSerializer)
                                                    && node != null
                                                    && node.StorageHandle.Equals(entry.Handle.StoreHandle));

Because the StorageHandle equality returns false, which in turn happens because the Unique of the two root Storage Handles are different, and not just incremental different, they are two different randoms. 因为StorageHandle相等性返回false,这又是由于两个根存储句柄的Unique性不同而不仅仅是增量不同而引起的,它们是两个不同的随机数。 The root of the problem is the behavior of NodeCacheNormal . 问题的根源是NodeCacheNormal的行为。

After creation of the tree in the first run, when the tree is loaded in the second execution for the first time, it is done through the LoadStorage() call, which simply sets the _root.Node to the RootNode read from the storage. 在第一次运行中创建树后,第一次在第二次执行中加载树时,将通过LoadStorage()调用完成该过程,该调用将_root.Node设置为从存储读取的RootNode This read RootNode contains the Unique of the last execution time which was committed to the disk and is never compared with the Unique of the new Root Handle created in this execution until the tree rollbacks. 读取的RootNode包含上一次执行到磁盘的执行时间的Unique性,并且从不与在此执行中创建的新Root Handle的Unique进行比较,直到树回滚为止。

The rollback causes the cache to clear, thereby causing the RootNode in the cache to be cleared off. 回滚导致清除缓存,从而清除缓存中的RootNode。 After rollback, if the RootNode is accessed again, it is fetched from the store and inserted in to cache except this time, its done through the main NodePin Lock(NodePin parent, LockType ltype, NodeHandle child) call which checks for the equality of handles in the above call. 回滚后,如果再次访问RootNode,则这次从存储中取出并插入到缓存中,但这次是通过主NodePin Lock(NodePin parent, LockType ltype, NodeHandle child)调用完成的,该调用检查句柄是否相等在上述通话中。

Are there any known fixes for this? 是否有任何已知的修复程序? The cache is pretty baked into the implementation, and I can't exactly find a good workaround to this. 缓存已经很好地融入了实现中,我无法找到一个很好的解决方法。

Edit 1: 编辑1:

Here's a class to produce the bug: 这是产生该错误的类:

 public class TreeBugTest
    {
        private BPlusTree<long, long> _tree;

        public TreeBugTest()
        {
            var options = new BPlusTree<long, long>.OptionsV2(new PrimitiveSerializer(), new PrimitiveSerializer());
            options.BTreeOrder = 4;
            options.CachePolicy = CachePolicy.Recent;
            options.CallLevelLock = new SimpleReadWriteLocking();
            options.FileName = ConfigurationSettings.AppSettings["Path"];
            options.CreateFile = CreatePolicy.IfNeeded;
            options.StorageType = StorageType.Disk;
            options.LockingFactory = new LockFactory<WriterOnlyLocking>();
            options.StoragePerformance = StoragePerformance.Default;
            _tree = new BPlusTree<long, long>(options);
        }

        public void AddSomeData(long start, long end)
        {
            while (start <= end)
            {
                _tree.Add(start, start++);
            }
        }

        public void ProduceBug()
        {
            AddSomeData(1, 1000);
            _tree.Commit();
            AddSomeData(1001,2000);
            _tree.Rollback();
            AddSomeData(2001, 3000); //This is where it occurs
            _tree.Commit();
        }
    }

Just provide a filepath, create its instance and call the ProduceBug() method. 只需提供一个文件路径,创建其实例并调用ProduceBug()方法即可。

Okay, so apparently the code is buggy. 好的,显然代码是错误的。 The handle comparison needs to be skipped for newly created root node. 对于新创建的根节点,需要跳过句柄比较。 For this, I moved the logic of Lock method to a private LockInternal method with signature: 为此,我将Lock方法的逻辑移到了带签名的私有LockInternal方法中:

private NodePin LockInternal(NodePin parent, LockType ltype, NodeHandle child, bool ignoreHandleComparison)

and changed the statements: 并更改了以下语句:

InvalidNodeHandleException.Assert(Storage.TryGetNode(child.StoreHandle, out node, NodeSerializer) && node != null && node.StorageHandle.Equals(entry.Handle.StoreHandle));

to: 至:

InvalidNodeHandleException.Assert(Storage.TryGetNode(child.StoreHandle, out node, NodeSerializer) && node != null && ignoreHandleComparison?true:node.StorageHandle.Equals(entry.Handle.StoreHandle));

And called this private LockInternal method from the original Lock method with a false for ignoreHandleComparison . 并从原始Lock方法调用此私有LockInternal方法,并将falseignoreHandleComparison The second place I modified was the LockRoot method where initially, I made the method virtual and in the override of NodeCacheNormal , I changed the call from Lock to LockInternal with a true for ignoreHandleComparison . 我修改了第二位的是LockRoot方法,其中最初,我提出的方法和虚拟中的超驰NodeCacheNormal ,我改变从呼叫LockLockInternaltrue用于ignoreHandleComparison And this worked for me. 这对我有用。

I guess I'll make a pull request for this fix to the repository of the project. 我想我会向此项目的存储库提出此修复请求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM