mirror of
https://github.com/ZoneTree/ZoneTree.git
synced 2026-10-20 15:51:49 +08:00
Fixes internal BTree key duplication.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Tenray.ZoneTree.Comparers;
|
||||
using Tenray.ZoneTree.Collections.BplusTree.Lock;
|
||||
using Tenray.ZoneTree.Comparers;
|
||||
using Tenray.ZoneTree.Serializers;
|
||||
|
||||
namespace Tenray.ZoneTree.UnitTests;
|
||||
@@ -289,16 +290,16 @@ public class IteratorTests
|
||||
zoneTree.Maintenance.DestroyTree();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void IntIntReverseIteratorParallelInserts()
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void IntIntReverseIteratorParallelInserts(bool reverse)
|
||||
{
|
||||
var dataPath = "data/IntIntReverseIteratorParallelInserts";
|
||||
if (Directory.Exists(dataPath))
|
||||
Directory.Delete(dataPath, true);
|
||||
|
||||
var random = new Random();
|
||||
var insertCount = 100000;
|
||||
var insertCount = 1000000;
|
||||
var iteratorCount = 1000;
|
||||
|
||||
using var zoneTree = new ZoneTreeFactory<int, int>()
|
||||
@@ -308,36 +309,45 @@ public class IteratorTests
|
||||
.SetWriteAheadLogDirectory(dataPath)
|
||||
.SetKeySerializer(new Int32Serializer())
|
||||
.SetValueSerializer(new Int32Serializer())
|
||||
.Configure(x =>
|
||||
{
|
||||
x.BTreeLockMode = BTreeLockMode.NodeLevelMonitor;
|
||||
})
|
||||
.OpenOrCreate();
|
||||
|
||||
var task = Task.Factory.StartNew(() =>
|
||||
var task = Task.Run(() =>
|
||||
{
|
||||
Parallel.For(0, insertCount, (x) =>
|
||||
{
|
||||
var key = random.Next();
|
||||
var key = random.Next(0, 100_000);
|
||||
zoneTree.Upsert(key, key + key);
|
||||
});
|
||||
});
|
||||
Parallel.For(0, iteratorCount, (x) =>
|
||||
{
|
||||
var initialCount = zoneTree.Maintenance.InMemoryRecordCount;
|
||||
using var iterator = zoneTree.CreateReverseIterator(false);
|
||||
var initialCount = zoneTree.Maintenance.MutableSegmentRecordCount;
|
||||
using var iterator =
|
||||
reverse ?
|
||||
zoneTree.CreateReverseIterator(false) :
|
||||
zoneTree.CreateIterator(false);
|
||||
iterator.SeekFirst();
|
||||
var counter = 1;
|
||||
var isValidData = true;
|
||||
var previousKey = int.MaxValue;
|
||||
var previousKey = reverse ? int.MaxValue : int.MinValue;
|
||||
while (iterator.Next())
|
||||
{
|
||||
var expected = iterator.CurrentKey + iterator.CurrentKey;
|
||||
if (iterator.CurrentValue != expected)
|
||||
isValidData = false;
|
||||
if (iterator.CurrentKey >= previousKey)
|
||||
if (reverse && iterator.CurrentKey >= previousKey)
|
||||
throw new Exception("Reverse Iterator is not iterating in valid order.");
|
||||
|
||||
if (!reverse && iterator.CurrentKey <= previousKey)
|
||||
throw new Exception("Iterator is not iterating in valid order.");
|
||||
|
||||
previousKey = iterator.CurrentKey;
|
||||
++counter;
|
||||
}
|
||||
if (counter < initialCount)
|
||||
Console.WriteLine(initialCount + " > " + counter);
|
||||
Assert.That(counter, Is.GreaterThanOrEqualTo(initialCount));
|
||||
Assert.That(isValidData, Is.True);
|
||||
});
|
||||
|
||||
@@ -245,4 +245,92 @@ public class SafeBTreeTests
|
||||
|
||||
task.Wait();
|
||||
}
|
||||
|
||||
[TestCase(BTreeLockMode.TopLevelReaderWriter)]
|
||||
[TestCase(BTreeLockMode.TopLevelMonitor)]
|
||||
[TestCase(BTreeLockMode.NodeLevelReaderWriter)]
|
||||
[TestCase(BTreeLockMode.NodeLevelMonitor)]
|
||||
public void IntIntDuplicateRecords(BTreeLockMode lockMode)
|
||||
{
|
||||
var random = new Random();
|
||||
var insertCount = 1000000;
|
||||
var iteratorCount = 1000;
|
||||
|
||||
var tree = new BTree<int, int>(new Int32ComparerAscending(), lockMode);
|
||||
var task = Task.Run(() =>
|
||||
{
|
||||
Parallel.For(0, insertCount, (x) =>
|
||||
{
|
||||
var key = random.Next(0, 100000);
|
||||
tree.Upsert(key, key + key, out _);
|
||||
});
|
||||
});
|
||||
|
||||
Parallel.For(0, iteratorCount, (x) =>
|
||||
{
|
||||
var initialCount = tree.Length;
|
||||
var iterator = new BTreeSeekableIterator<int, int>(tree);
|
||||
var counter = iterator.SeekBegin() ? 1 : 0;
|
||||
var isValidData = true;
|
||||
var previousKey = int.MinValue;
|
||||
while (iterator.Next())
|
||||
{
|
||||
var expected = iterator.CurrentKey + iterator.CurrentKey;
|
||||
if (iterator.CurrentValue != expected)
|
||||
isValidData = false;
|
||||
if (iterator.CurrentKey <= previousKey)
|
||||
throw new Exception(
|
||||
$"Iterator is not iterating in valid order.{iterator.CurrentKey} <= {previousKey}");
|
||||
previousKey = iterator.CurrentKey;
|
||||
++counter;
|
||||
}
|
||||
Assert.That(counter, Is.GreaterThanOrEqualTo(initialCount));
|
||||
Assert.That(isValidData, Is.True);
|
||||
});
|
||||
task.Wait();
|
||||
}
|
||||
|
||||
[TestCase(BTreeLockMode.TopLevelReaderWriter)]
|
||||
[TestCase(BTreeLockMode.TopLevelMonitor)]
|
||||
[TestCase(BTreeLockMode.NodeLevelReaderWriter)]
|
||||
[TestCase(BTreeLockMode.NodeLevelMonitor)]
|
||||
public void IntIntDuplicateReverseRecords(BTreeLockMode lockMode)
|
||||
{
|
||||
var random = new Random();
|
||||
var insertCount = 1000000;
|
||||
var iteratorCount = 1000;
|
||||
|
||||
var tree = new BTree<int, int>(new Int32ComparerAscending(), lockMode);
|
||||
var task = Task.Run(() =>
|
||||
{
|
||||
Parallel.For(0, insertCount, (x) =>
|
||||
{
|
||||
var key = random.Next(0, 100000);
|
||||
tree.Upsert(key, key + key, out _);
|
||||
});
|
||||
});
|
||||
|
||||
Parallel.For(0, iteratorCount, (x) =>
|
||||
{
|
||||
var initialCount = tree.Length;
|
||||
var iterator = new BTreeSeekableIterator<int, int>(tree);
|
||||
var counter = iterator.SeekEnd() ? 1 : 0;
|
||||
var isValidData = true;
|
||||
var previousKey = int.MaxValue;
|
||||
while (iterator.Prev())
|
||||
{
|
||||
var expected = iterator.CurrentKey + iterator.CurrentKey;
|
||||
if (iterator.CurrentValue != expected)
|
||||
isValidData = false;
|
||||
if (iterator.CurrentKey >= previousKey)
|
||||
throw new Exception(
|
||||
$"Reverse Iterator is not iterating in valid order.{iterator.CurrentKey} >= {previousKey}");
|
||||
previousKey = iterator.CurrentKey;
|
||||
++counter;
|
||||
}
|
||||
Assert.That(counter, Is.GreaterThanOrEqualTo(initialCount));
|
||||
Assert.That(isValidData, Is.True);
|
||||
});
|
||||
task.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,8 @@ public partial class BTree<TKey, TValue>
|
||||
node.WriteUnlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (found)
|
||||
++position;
|
||||
var child = node.Children[position];
|
||||
child.WriteLock();
|
||||
if (child.IsFull)
|
||||
@@ -208,7 +209,7 @@ public partial class BTree<TKey, TValue>
|
||||
SplitChild(node, position, child);
|
||||
child.WriteUnlock();
|
||||
|
||||
if (Comparer.Compare(in key, in node.Keys[position]) > 0)
|
||||
if (Comparer.Compare(in key, in node.Keys[position]) >= 0)
|
||||
++position;
|
||||
|
||||
child = node.Children[position];
|
||||
@@ -240,6 +241,9 @@ public partial class BTree<TKey, TValue>
|
||||
return true;
|
||||
}
|
||||
|
||||
if (found)
|
||||
++position;
|
||||
|
||||
var child = node.Children[position];
|
||||
child.WriteLock();
|
||||
if (child.IsFull)
|
||||
@@ -247,7 +251,7 @@ public partial class BTree<TKey, TValue>
|
||||
SplitChild(node, position, child);
|
||||
child.WriteUnlock();
|
||||
|
||||
if (Comparer.Compare(in key, in node.Keys[position]) > 0)
|
||||
if (Comparer.Compare(in key, in node.Keys[position]) >= 0)
|
||||
++position;
|
||||
|
||||
child = node.Children[position];
|
||||
@@ -282,6 +286,9 @@ public partial class BTree<TKey, TValue>
|
||||
return AddOrUpdateResult.ADDED;
|
||||
}
|
||||
|
||||
if (found)
|
||||
++position;
|
||||
|
||||
var child = node.Children[position];
|
||||
child.WriteLock();
|
||||
if (child.IsFull)
|
||||
@@ -289,7 +296,7 @@ public partial class BTree<TKey, TValue>
|
||||
SplitChild(node, position, child);
|
||||
child.WriteUnlock();
|
||||
|
||||
if (Comparer.Compare(in key, in node.Keys[position]) > 0)
|
||||
if (Comparer.Compare(in key, in node.Keys[position]) >= 0)
|
||||
++position;
|
||||
|
||||
child = node.Children[position];
|
||||
|
||||
Reference in New Issue
Block a user