/* 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 Dec 3, 2008 */ package com.bigdata.btree; import java.io.File; import java.util.UUID; import com.bigdata.btree.keys.TestKeyBuilder; import com.bigdata.rawstore.SimpleMemoryRawStore; /** * A test of the {@link IndexSegmentBuilder} in which there are some deleted * tuples in the source {@link BTree} and a compacting merge is performed. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class TestIndexSegmentBuilderWithCompactingMerge extends AbstractIndexSegmentTestCase { /** * */ public TestIndexSegmentBuilderWithCompactingMerge() { } /** * @param name */ public TestIndexSegmentBuilderWithCompactingMerge(String name) { super(name); } File outFile; File tmpDir; public void setUp() throws Exception { super.setUp(); outFile = new File(getName() + ".seg"); if (outFile.exists() && !outFile.delete()) { throw new RuntimeException("Could not delete file: " + outFile); } tmpDir = outFile.getAbsoluteFile().getParentFile(); } public void tearDown() throws Exception { if (outFile != null && outFile.exists() && !outFile.delete()) { log.warn("Could not delete file: " + outFile); } super.tearDown(); } /** * Unit test explores the correct propagation of deleted index entries to * the generated index segment. * * @throws Exception * * @todo modify test to verify correct propagation of the version timestamps * (or create an entire test suite for that purpose). Note that * version timestamps are only used for full transactions. */ public void test_deletedEntries() throws Exception { final long createTime = System.currentTimeMillis(); final String name = "testIndex"; final IndexMetadata metadata = new IndexMetadata(name,UUID.randomUUID()); // enable delete markers. metadata.setDeleteMarkers(true); // use the same branching factor on the BTree and the IndexSegment for // ease of comparison. metadata.setBranchingFactor(3); metadata.setIndexSegmentBranchingFactor(3); final BTree expected = BTree.create(new SimpleMemoryRawStore(), metadata); for (int i = 1; i <= 10; i++) { expected.insert(TestKeyBuilder.asSortKey(i), new SimpleEntry(i)); } /* * First, build an index segment from the original BTree. */ { final byte[] fromKey = null; final byte[] toKey = null; // should cover all keys. assertEquals(10, expected.getEntryCount()); assertEquals(10, expected.rangeCount(fromKey, toKey)); assertEquals(10, expected.rangeCountExact(fromKey, toKey)); final IndexSegmentBuilder builder = IndexSegmentBuilder .newInstance(/*name,*/ expected, outFile, tmpDir, true/* compactingMerge */, createTime, fromKey, toKey); IndexSegmentStore segmentStore = null; try { @SuppressWarnings("unused") final IndexSegmentCheckpoint checkpoint = builder.call(); // @see BLZG-1501 (remove LRUNexus) // if (LRUNexus.INSTANCE != null) { // // /* // * Clear the records for the index segment from the cache so we will // * read directly from the file. This is necessary to ensure that the // * data on the file is good rather than just the data in the cache. // */ // // LRUNexus.INSTANCE.deleteCache(checkpoint.segmentUUID); // // } segmentStore = new IndexSegmentStore(outFile); final IndexSegment actual = segmentStore.loadIndexSegment(); assertSameEntryIterator(expected.rangeIterator(fromKey, toKey), actual.rangeIterator(fromKey, toKey)); /* * The index segment's iterator WITH the DELETED tuples should * visit the same tuples in the same order as the ground truth * BTree's iterator WITHOUT the DELETED tuples (because the * deleted tuples SHOULD NOT have been copied into the generated * index segment). */ assertSameEntryIterator( expected .rangeIterator(fromKey, toKey, 0/* capacity */, IRangeQuery.DEFAULT, null/* filter */), actual .rangeIterator(fromKey, toKey, 0/* capacity */, IRangeQuery.DEFAULT | IRangeQuery.DELETED, null/* filter */)); } finally { if (segmentStore != null) segmentStore.destroy(); outFile.delete(); } } /* * Now delete a few tuples. */ { final byte[] fromKey = null; final byte[] toKey = null; expected.remove(TestKeyBuilder.asSortKey(3)); expected.remove(TestKeyBuilder.asSortKey(5)); // should cover all keys (since deleted tuples are still present). assertEquals(10, expected.getEntryCount()); // should cover all keys (since deleted tuples are still present). assertEquals(10, expected.rangeCount(fromKey, toKey)); // should exclude the deleted tuples. assertEquals(8, expected.rangeCountExact(fromKey, toKey)); final IndexSegmentBuilder builder = IndexSegmentBuilder .newInstance(/*name,*/ expected, outFile, tmpDir, true/* compactingMerge */, createTime, fromKey, toKey); IndexSegmentStore segmentStore = null; try { @SuppressWarnings("unused") final IndexSegmentCheckpoint checkpoint = builder.call(); // @see BLZG-1501 (remove LRUNexus) // if (LRUNexus.INSTANCE != null) { // // /* // * Clear the records for the index segment from the cache so we will // * read directly from the file. This is necessary to ensure that the // * data on the file is good rather than just the data in the cache. // */ // // LRUNexus.INSTANCE.deleteCache(checkpoint.segmentUUID); // // } segmentStore = new IndexSegmentStore(outFile); final IndexSegment actual = segmentStore.loadIndexSegment(); assertSameEntryIterator(expected.rangeIterator(fromKey, toKey), actual.rangeIterator(fromKey, toKey)); /* * The index segment's iterator WITH the DELETED tuples should * visit the same tuples in the same order as the ground truth * BTree's iterator WITHOUT the DELETED tuples (because the * deleted tuples SHOULD NOT have been copied into the generated * index segment). */ assertSameEntryIterator( expected .rangeIterator(fromKey, toKey, 0/* capacity */, IRangeQuery.DEFAULT, null/* filter */), actual .rangeIterator(fromKey, toKey, 0/* capacity */, IRangeQuery.DEFAULT | IRangeQuery.DELETED, null/* filter */)); } finally { if (segmentStore != null) segmentStore.destroy(); outFile.delete(); } } /* * Now delete all the tuples. */ { final byte[] fromKey = null; final byte[] toKey = null; /* * Note: removeAll() MUST write delete markers when the index * supports them rather than just replacing the root with a new root * leaf. */ expected.removeAll(); // should cover all keys (since deleted tuples are still present). assertEquals(10, expected.getEntryCount()); // should cover all keys (since deleted tuples are still present). assertEquals(10, expected.rangeCount(fromKey, toKey)); // should exclude the deleted tuples. assertEquals(0, expected.rangeCountExact(fromKey, toKey)); final IndexSegmentBuilder builder = IndexSegmentBuilder .newInstance(/*name, */ expected, outFile, tmpDir, true/* compactingMerge */, createTime, fromKey, toKey); IndexSegmentStore segmentStore = null; try { @SuppressWarnings("unused") final IndexSegmentCheckpoint checkpoint = builder.call(); // @see BLZG-1501 (remove LRUNexus) // if (LRUNexus.INSTANCE != null) { // // /* // * Clear the records for the index segment from the cache so we will // * read directly from the file. This is necessary to ensure that the // * data on the file is good rather than just the data in the cache. // */ // // LRUNexus.INSTANCE.deleteCache(checkpoint.segmentUUID); // // } segmentStore = new IndexSegmentStore(outFile); final IndexSegment actual = segmentStore.loadIndexSegment(); assertSameEntryIterator(expected.rangeIterator(fromKey, toKey), actual.rangeIterator(fromKey, toKey)); /* * The index segment's iterator WITH the DELETED tuples should * visit the same tuples in the same order as the ground truth * BTree's iterator WITHOUT the DELETED tuples (because the * deleted tuples SHOULD NOT have been copied into the generated * index segment). */ assertSameEntryIterator( expected .rangeIterator(fromKey, toKey, 0/* capacity */, IRangeQuery.DEFAULT, null/* filter */), actual .rangeIterator(fromKey, toKey, 0/* capacity */, IRangeQuery.DEFAULT | IRangeQuery.DELETED, null/* filter */)); } finally { if (segmentStore != null) segmentStore.destroy(); outFile.delete(); } } } }