一段时间以来,我一直在使用B Plus Tree 的这种实现。我注意到“最近”缓存有问题。以下是该错误的产生方式:
- 我添加了一些 KVP,并提交了树。
- 我添加了更多的 KVP 并回滚了树。
- 我添加了更多的 KVP 并提交了树。
- 我重新启动我的应用程序并重复步骤 1,2 和 3
树在重新启动后执行第 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
提交到磁盘的最后执行时间,并且永远不会与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()
方法。