/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Nov 18, 2006 */ package com.bigdata.btree; import java.util.UUID; import com.bigdata.btree.keys.TestKeyBuilder; import com.bigdata.cache.HardReferenceQueue; import com.bigdata.rawstore.IRawStore; import com.bigdata.rawstore.SimpleMemoryRawStore; /** * Test suite for the logic performing incremental writes of nodes and leaves * onto the store. The actual timing of evictions from the * {@link HardReferenceQueue} is essentially unpredictable since evictions are * driven by {@link AbstractBTree#touch(AbstractNode)} and nodes and leaves are * both touched frequently and in a data and code path dependent manner. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class TestIncrementalWrite extends AbstractBTreeTestCase { /** * */ public TestIncrementalWrite() { } /** * @param name */ public TestIncrementalWrite(String name) { super(name); } protected BTree getBTree(int branchingFactor, final int queueCapacity, final int queueScan) { IRawStore store = new SimpleMemoryRawStore(); IndexMetadata md = new IndexMetadata(UUID.randomUUID()); md.setBranchingFactor(branchingFactor); /* * Note: This jumps through hoops to create the BTree instance with the * appropriate parameterization of the hard reference queue. */ // // Note: override the btree class. // md.setClassName(TestBTree.class.getName()); md.write(store); Checkpoint checkpoint = md.firstCheckpoint(); checkpoint.write(store); BTree btree = new TestBTree(store, checkpoint, md, false/*readOnly*/) { @Override int getQueueCapacity() { return queueCapacity; } @Override int getQueueScan() { return queueScan; } }; // BTree btree = new BTree(store, // branchingFactor, // UUID.randomUUID(), // false,//isolatable // null,//conflictResolver // new MyHardReferenceQueue<PO>(new DefaultEvictionListener(), // queueCapacity, queueScan),// // KeyBufferSerializer.INSTANCE,// // ByteArrayValueSerializer.INSTANCE, // // null // no record compressor // ); return btree; } /** * Custom hard reference queue. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ private abstract static class TestBTree extends BTree { abstract int getQueueCapacity(); abstract int getQueueScan(); /** * @param store * @param checkpoint * @param metadata */ public TestBTree(IRawStore store, Checkpoint checkpoint, IndexMetadata metadata, boolean readOnly) { super(store, checkpoint, metadata, readOnly); } protected HardReferenceQueue<PO> newWriteRetentionQueue(final boolean readOnly) { return new MyHardReferenceQueue<PO>(// new DefaultEvictionListener(),// getQueueCapacity(),// getQueueScan()// ); } } /** * Test verifies that an incremental write of the root leaf may be * performed. */ public void test_incrementalWrite() { /* * setup the tree. it uses a queue capacity of two since that is the * minimum allowed. it uses scanning to ensure that one a single * reference to the root leaf actually enters the queue. that way when * we request an incremental write it occurs since the reference counter * for the root leaf will be one (1) since there is only one reference * to that leaf on the queue. */ BTree btree = getBTree(3,2,1); final SimpleEntry v3 = new SimpleEntry(3); final SimpleEntry v5 = new SimpleEntry(5); final SimpleEntry v7 = new SimpleEntry(7); /* * insert some keys into the root leaf. */ final Leaf a = (Leaf)btree.getRoot(); btree.insert(TestKeyBuilder.asSortKey(3), v3); btree.insert(TestKeyBuilder.asSortKey(5), v5); btree.insert(TestKeyBuilder.asSortKey(7), v7); /* * do an incremental write of the root leaf. */ assertFalse( a.isPersistent() ); ((HardReferenceQueue<PO>) btree.writeRetentionQueue).getListener() .evicted(((HardReferenceQueue<PO>) btree.writeRetentionQueue), btree.getRoot()); assertTrue(a.isPersistent()); } /** * Test verifies that an incremental write of a leaf may be performed, that * identity is assigned to the written leaf, and that the childKey[] on the * parent node is updated to reflect the identity assigned to the leaf. */ public void test_incrementalWrite02() { /* * setup the tree with a most queue capacity but set the scan parameter * such that we never allow more than a single reference to a node onto * the queue. */ BTree btree = getBTree(3,20,20); /* * insert keys into the root and cause it to split. */ final SimpleEntry v3 = new SimpleEntry(3); final SimpleEntry v5 = new SimpleEntry(5); final SimpleEntry v7 = new SimpleEntry(7); final SimpleEntry v9 = new SimpleEntry(9); final Leaf a = (Leaf) btree.getRoot(); btree.insert(TestKeyBuilder.asSortKey(3),v3); btree.insert(TestKeyBuilder.asSortKey(5),v5); btree.insert(TestKeyBuilder.asSortKey(7),v7); btree.insert(TestKeyBuilder.asSortKey(9),v9); assertNotSame(a,btree.getRoot()); final Node c = (Node) btree.getRoot(); assertKeys(new int[]{7},c); assertEquals(a,c.getChild(0)); final Leaf b = (Leaf) c.getChild(1); assertKeys(new int[]{3,5},a); assertValues(new Object[]{v3,v5}, a); assertKeys(new int[]{7,9},b); assertValues(new Object[]{v7,v9}, b); /* * verify reference counters. */ assertEquals(1,a.referenceCount); assertEquals(1,b.referenceCount); assertEquals(1,c.referenceCount); /* * verify that all nodes are NOT persistent. */ assertFalse(a.isPersistent()); assertFalse(b.isPersistent()); assertFalse(c.isPersistent()); /* * verify the queue order. we know the queue order since no node is * allowed into the queue more than once (because the scan parameter is * equal to the queue capacity) and because we know the node creation * order (a is created when the tree is created; b is created when a is * split; and c is created after the split when we discovert that there * is no parent of a and that we need to create one). */ assertEquals(new PO[] { a, b, c }, ((MyHardReferenceQueue<PO>) btree.writeRetentionQueue) .toArray(new PO[0])); /* * force (b) to be evicted. since its reference count is one(1) it will * be made persistent. * * Note: this causes the reference counter for (b) to be reduced to * zero(0) even through (b) is on the queue. This is not a legal state * so we can not continue with operation that would touch the queue. */ ((HardReferenceQueue<PO>) btree.writeRetentionQueue).getListener().evicted( ((HardReferenceQueue<PO>) btree.writeRetentionQueue), b); // verify that b is now persistent. assertTrue(b.isPersistent()); // verify that we set the identity of b on its parent so that it can be // recovered from the store if necessary. assertEquals(b.getIdentity(), c.getChildAddr(1)); } /** * Test verifies that an incremental write of a node may be performed, that * identity is assigned to the written node, and that the childKey[] on the * node are updated to reflect the identity assigned to its children (the * dirty children are written out when the node is evicted so that the * persistent node knows the persistent identity of each child). */ public void test_incrementalWrite03() { /* * setup the tree with a most queue capacity but set the scan parameter * such that we never allow more than a single reference to a node onto * the queue. */ BTree btree = getBTree(3,20,20); /* * insert keys into the root and cause it to split. */ final SimpleEntry v3 = new SimpleEntry(3); final SimpleEntry v5 = new SimpleEntry(5); final SimpleEntry v7 = new SimpleEntry(7); final SimpleEntry v9 = new SimpleEntry(9); final Leaf a = (Leaf) btree.getRoot(); btree.insert(TestKeyBuilder.asSortKey(3),v3); btree.insert(TestKeyBuilder.asSortKey(5),v5); btree.insert(TestKeyBuilder.asSortKey(7),v7); btree.insert(TestKeyBuilder.asSortKey(9),v9); assertNotSame(a,btree.getRoot()); final Node c = (Node) btree.getRoot(); assertKeys(new int[]{7},c); assertEquals(a,c.getChild(0)); final Leaf b = (Leaf) c.getChild(1); assertKeys(new int[]{3,5},a); assertValues(new Object[]{v3,v5}, a); assertKeys(new int[]{7,9},b); assertValues(new Object[]{v7,v9}, b); /* * verify reference counters. */ assertEquals(1,a.referenceCount); assertEquals(1,b.referenceCount); assertEquals(1,c.referenceCount); /* * verify that all nodes are NOT persistent. */ assertFalse(a.isPersistent()); assertFalse(b.isPersistent()); assertFalse(c.isPersistent()); /* * verify the queue order. we know the queue order since no node is * allowed into the queue more than once (because the scan parameter is * equal to the queue capacity) and because we know the node creation * order (a is created when the tree is created; b is created when a is * split; and c is created after the split when we discover that there * is no parent of a and that we need to create one). */ assertEquals(new PO[] { a, b, c }, ((MyHardReferenceQueue<PO>) btree.writeRetentionQueue) .toArray(new PO[0])); /* * force (c) to be evicted. since its reference count is one(1) it will * be made persistent. * * Note: this causes the reference counter for (c) to be reduced to * zero(0) even through (c) is on the queue. This is not a legal state * so we can not continue with operations that would touch the queue. */ ((HardReferenceQueue<PO>) btree.writeRetentionQueue).getListener() .evicted(((HardReferenceQueue<PO>) btree.writeRetentionQueue), c); // verify that c and its children (a,b) are now persistent. assertTrue(c.isPersistent()); assertTrue(a.isPersistent()); assertTrue(b.isPersistent()); // verify that we set the identity of (a,b) on their parent (c). assertEquals(a.getIdentity(), c.getChildAddr(0)); assertEquals(b.getIdentity(), c.getChildAddr(1)); } }