use of org.apache.ignite.internal.pagememory.tree.IgniteTree.OperationType.PUT in project ignite-3 by apache.
the class ItBplusTreeSelfTest method testSizeForPutRmvSequential.
/**
* Verifies that {@link BplusTree#size} and {@link BplusTree#size} methods behave correctly on single-threaded addition and removal of
* elements in random order.
*
* @throws Exception If failed.
*/
@Test
public void testSizeForPutRmvSequential() throws Exception {
MAX_PER_PAGE = 5;
boolean debugPrint = false;
int itemCnt = (int) Math.pow(MAX_PER_PAGE, 5) + rnd.nextInt(MAX_PER_PAGE * MAX_PER_PAGE);
Long[] items = new Long[itemCnt];
for (int i = 0; i < itemCnt; ++i) {
items[i] = (long) i;
}
TestTree testTree = createTestTree(true);
TreeMap<Long, Long> goldenMap = new TreeMap<>();
assertEquals(0, testTree.size());
assertEquals(0, goldenMap.size());
final Predicate<Long> rowMatcher = row -> row % 7 == 0;
final TreeRowClosure<Long, Long> rowClosure = (tree, io, pageAddr, idx) -> rowMatcher.test(io.getLookupRow(tree, pageAddr, idx));
int correctMatchingRows = 0;
shuffle(Arrays.asList(items), rnd);
for (Long row : items) {
if (debugPrint) {
println(" --> put(" + row + ")");
print(testTree.printTree());
}
assertEquals(goldenMap.put(row, row), testTree.put(row));
assertEquals(row, testTree.findOne(row));
if (rowMatcher.test(row)) {
++correctMatchingRows;
}
assertEquals(correctMatchingRows, testTree.size(rowClosure));
long correctSize = goldenMap.size();
assertEquals(correctSize, testTree.size());
assertEquals(correctSize, size(testTree.find(null, null)));
assertNoLocks();
}
shuffle(Arrays.asList(items), rnd);
for (Long row : items) {
if (debugPrint) {
println(" --> rmv(" + row + ")");
print(testTree.printTree());
}
assertEquals(row, goldenMap.remove(row));
assertEquals(row, testTree.remove(row));
assertNull(testTree.findOne(row));
if (rowMatcher.test(row)) {
--correctMatchingRows;
}
assertEquals(correctMatchingRows, testTree.size(rowClosure));
long correctSize = goldenMap.size();
assertEquals(correctSize, testTree.size());
assertEquals(correctSize, size(testTree.find(null, null)));
assertNoLocks();
}
}
use of org.apache.ignite.internal.pagememory.tree.IgniteTree.OperationType.PUT in project ignite-3 by apache.
the class ItBplusTreeSelfTest method testPutSizeLivelock.
/**
* The test forces {@link BplusTree#size} method to run into a livelock: during single run the method is picking up new pages which are
* concurrently added to the tree until the new pages are not added anymore. Test verifies that despite livelock condition a size from a
* valid range is returned.
*
* <p>NB: This test has to be changed with the integration of IGNITE-3478.
*
* @throws Exception if test failed
*/
@Test
public void testPutSizeLivelock() throws Exception {
MAX_PER_PAGE = 5;
CNT = 800;
final int slidingWindowSize = 16;
final boolean debugPrint = false;
final TestTree tree = createTestTree(false);
final AtomicLong curRmvKey = new AtomicLong(0);
final AtomicLong curPutKey = new AtomicLong(slidingWindowSize);
for (long i = curRmvKey.get(); i < curPutKey.get(); ++i) {
assertNull(tree.put(i));
}
final int hwThreads = CPUS;
final int putRmvThreadCnt = Math.max(1, hwThreads / 2);
final int sizeThreadCnt = hwThreads - putRmvThreadCnt;
final CyclicBarrier putRmvOpBarrier = new CyclicBarrier(putRmvThreadCnt, () -> {
if (debugPrint) {
try {
println("===BARRIER=== size=" + tree.size() + " [" + tree.findFirst() + ".." + tree.findLast() + "]");
} catch (IgniteInternalCheckedException e) {
// ignore
}
}
});
final int loopCnt = CNT / hwThreads;
CompletableFuture<?> putRmvFut = runMultiThreadedAsync(() -> {
for (int i = 0; i < loopCnt && !stop.get(); ++i) {
int order;
try {
order = putRmvOpBarrier.await();
} catch (BrokenBarrierException e) {
// barrier reset() has been called: terminate
break;
}
Long putVal = curPutKey.getAndIncrement();
if ((i & 0xff) == 0) {
println(order + ": --> put(" + putVal + ")");
}
assertNull(tree.put(putVal));
Long rmvVal = curRmvKey.getAndIncrement();
if ((i & 0xff) == 0) {
println(order + ": --> rmv(" + rmvVal + ")");
}
assertEquals(rmvVal, tree.remove(rmvVal));
assertNull(tree.findOne(rmvVal));
}
return null;
}, putRmvThreadCnt, "put-remove");
CompletableFuture<?> sizeFut = runMultiThreadedAsync(() -> {
final List<Long> treeContents = new ArrayList<>(slidingWindowSize * 2);
final TreeRowClosure<Long, Long> rowDumper = (tree1, io, pageAddr, idx) -> {
treeContents.add(io.getLookupRow(tree1, pageAddr, idx));
final long endMs = System.currentTimeMillis() + 10;
final long endPutKey = curPutKey.get() + MAX_PER_PAGE;
while (System.currentTimeMillis() < endMs && curPutKey.get() < endPutKey) {
Thread.yield();
}
return true;
};
while (!stop.get()) {
treeContents.clear();
long treeSize = tree.size(rowDumper);
long curPutVal = curPutKey.get();
println(" ======> size=" + treeSize + "; last-put-value=" + curPutVal);
if (treeSize < slidingWindowSize || treeSize > curPutVal) {
fail("Tree size is not in bounds [" + slidingWindowSize + ".." + curPutVal + "]:" + treeSize + "; contents=" + treeContents);
}
}
return null;
}, sizeThreadCnt, "size");
asyncRunFut = CompletableFuture.allOf(putRmvFut, sizeFut);
try {
putRmvFut.get(getTestTimeout(), MILLISECONDS);
} finally {
stop.set(true);
putRmvOpBarrier.reset();
asyncRunFut.get(getTestTimeout(), MILLISECONDS);
}
tree.validateTree();
assertNoLocks();
}
use of org.apache.ignite.internal.pagememory.tree.IgniteTree.OperationType.PUT in project ignite-3 by apache.
the class ItBplusTreeSelfTest method doTestSizeForRandomPutRmvMultithreadedAsync.
/**
* Verifies that concurrent running of {@link BplusTree#put} + {@link BplusTree#remove} sequence and {@link BplusTree#size} methods
* results in correct calculation of tree size.
*
* <p>Since in the presence of concurrent modifications the size may differ from the actual one, the test maintains sliding window of
* records in the tree, uses a barrier between concurrent runs to limit runaway delta in the calculated size, and checks that the
* measured size lies within certain bounds.
*
* <p>NB: This test has to be changed with the integration of IGNITE-3478.
*/
public void doTestSizeForRandomPutRmvMultithreadedAsync(final int rmvPutSlidingWindowSize) throws Exception {
MAX_PER_PAGE = 5;
final boolean debugPrint = false;
final TestTree tree = createTestTree(false);
final AtomicLong curRmvKey = new AtomicLong(0);
final AtomicLong curPutKey = new AtomicLong(rmvPutSlidingWindowSize);
for (long i = curRmvKey.get(); i < curPutKey.get(); ++i) {
assertNull(tree.put(i));
}
final int putRmvThreadCnt = Math.min(CPUS, rmvPutSlidingWindowSize);
final int sizeThreadCnt = putRmvThreadCnt;
final CyclicBarrier putRmvOpBarrier = new CyclicBarrier(putRmvThreadCnt + sizeThreadCnt, () -> {
if (debugPrint) {
try {
println("===BARRIER=== size=" + tree.size() + "; contents=[" + tree.findFirst() + ".." + tree.findLast() + "]" + "; rmvVal=" + curRmvKey.get() + "; putVal=" + curPutKey.get());
println(tree.printTree());
} catch (IgniteInternalCheckedException e) {
// ignore
}
}
});
final int loopCnt = 250;
CompletableFuture<?> putRmvFut = runMultiThreadedAsync(() -> {
for (int i = 0; i < loopCnt && !stop.get(); ++i) {
int order;
try {
order = putRmvOpBarrier.await();
} catch (BrokenBarrierException e) {
break;
}
Long putVal = curPutKey.getAndIncrement();
if (debugPrint || (i & 0x3ff) == 0) {
println(order + ": --> put(" + putVal + ")");
}
assertNull(tree.put(putVal));
Long rmvVal = curRmvKey.getAndIncrement();
if (debugPrint || (i & 0x3ff) == 0) {
println(order + ": --> rmv(" + rmvVal + ")");
}
assertEquals(rmvVal, tree.remove(rmvVal));
assertNull(tree.findOne(rmvVal));
}
return null;
}, putRmvThreadCnt, "put-remove");
CompletableFuture<?> sizeFut = runMultiThreadedAsync(() -> {
final List<Long> treeContents = new ArrayList<>(rmvPutSlidingWindowSize * 2);
final TreeRowClosure<Long, Long> rowDumper = (tree1, io, pageAddr, idx) -> {
treeContents.add(io.getLookupRow(tree1, pageAddr, idx));
return true;
};
for (long iter = 0; !stop.get(); ++iter) {
int order = 0;
try {
order = putRmvOpBarrier.await();
} catch (BrokenBarrierException e) {
break;
}
long correctSize = curPutKey.get() - curRmvKey.get();
treeContents.clear();
long treeSize = tree.size(rowDumper);
long minBound = correctSize - putRmvThreadCnt;
long maxBound = correctSize + putRmvThreadCnt;
if (debugPrint || (iter & 0x3ff) == 0) {
println(order + ": size=" + treeSize + "; bounds=[" + minBound + ".." + maxBound + "]; contents=" + treeContents);
}
if (treeSize < minBound || treeSize > maxBound) {
fail("Tree size is not in bounds [" + minBound + ".." + maxBound + "]: " + treeSize + "; Tree contents: " + treeContents);
}
}
return null;
}, sizeThreadCnt, "size");
CompletableFuture<?> lockPrintingFut = runMultiThreadedAsync(() -> {
while (!stop.get()) {
Thread.sleep(1_000);
println(TestTree.printLocks());
}
return null;
}, 1, "printLocks");
asyncRunFut = CompletableFuture.allOf(putRmvFut, sizeFut, lockPrintingFut);
try {
putRmvFut.get(getTestTimeout(), MILLISECONDS);
} finally {
stop.set(true);
// To ensure that an BrokenBarrierException is thrown on method CyclicBarrier#await in other threads.
while (!asyncRunFut.isDone()) {
putRmvOpBarrier.reset();
Thread.sleep(10);
}
asyncRunFut.get(getTestTimeout(), MILLISECONDS);
}
tree.validateTree();
assertNoLocks();
}
Aggregations