[英]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.