[英]CSharpTest.Net.Collections.BPlusTree RecentCache bug?
我使用B Plus Tree的这种实现已有一段时间了。 我注意到“最近”缓存存在错误。 错误产生的方式如下:
重新启动后执行步骤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)
以下断言失败。
InvalidNodeHandleException.Assert(Storage.TryGetNode(child.StoreHandle, out node, NodeSerializer)
&& node != null
&& node.StorageHandle.Equals(entry.Handle.StoreHandle));
因为StorageHandle相等性返回false,这又是由于两个根存储句柄的Unique
性不同而不仅仅是增量不同而引起的,它们是两个不同的随机数。 问题的根源是NodeCacheNormal
的行为。
在第一次运行中创建树后,第一次在第二次执行中加载树时,将通过LoadStorage()
调用完成该过程,该调用将_root.Node
设置为从存储读取的RootNode
。 读取的RootNode
包含上一次执行到磁盘的执行时间的Unique
性,并且从不与在此执行中创建的新Root Handle的Unique
进行比较,直到树回滚为止。
回滚导致清除缓存,从而清除缓存中的RootNode。 回滚后,如果再次访问RootNode,则这次从存储中取出并插入到缓存中,但这次是通过主NodePin Lock(NodePin parent, LockType ltype, NodeHandle child)
调用完成的,该调用检查句柄是否相等在上述通话中。
是否有任何已知的修复程序? 缓存已经很好地融入了实现中,我无法找到一个很好的解决方法。
编辑1:
这是产生该错误的类:
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();
}
}
只需提供一个文件路径,创建其实例并调用ProduceBug()
方法即可。
好的,显然代码是错误的。 对于新创建的根节点,需要跳过句柄比较。 为此,我将Lock
方法的逻辑移到了带签名的私有LockInternal
方法中:
private NodePin LockInternal(NodePin parent, LockType ltype, NodeHandle child, bool ignoreHandleComparison)
并更改了以下语句:
InvalidNodeHandleException.Assert(Storage.TryGetNode(child.StoreHandle, out node, NodeSerializer) && node != null && node.StorageHandle.Equals(entry.Handle.StoreHandle));
至:
InvalidNodeHandleException.Assert(Storage.TryGetNode(child.StoreHandle, out node, NodeSerializer) && node != null && ignoreHandleComparison?true:node.StorageHandle.Equals(entry.Handle.StoreHandle));
并从原始Lock
方法调用此私有LockInternal
方法,并将false
为ignoreHandleComparison
。 我修改了第二位的是LockRoot
方法,其中最初,我提出的方法和虚拟中的超驰NodeCacheNormal
,我改变从呼叫Lock
到LockInternal
与true
用于ignoreHandleComparison
。 这对我有用。
我想我会向此项目的存储库提出此修复请求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.