/** 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 */ package com.bigdata.htree; import java.io.IOException; import com.bigdata.btree.ITupleIterator; import com.bigdata.rawstore.IRawStore; import com.bigdata.rawstore.SimpleMemoryRawStore; /** * Test {@link HTree} with duplicate keys. * * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/763" > * Stochastic Results With Analytic Query Mode </a> */ public class TestDuplicates extends AbstractHTreeTestCase { /** * */ public TestDuplicates() { } /** * @param name */ public TestDuplicates(String name) { super(name); } // private static final boolean bufferNodes = true; /** * Tests the ability to store values against duplicate * keys. * * @throws IOException * @throws Exception */ public void test_duplicateKeys() throws IOException, Exception { final IRawStore store = new SimpleMemoryRawStore(); try { HTree htree = getHTree(store, 3/* addressBits */, false/* rawRecords */, true/* persistent */); final byte[] k1 = new byte[] { 1 }; final byte[] v2 = new byte[] { 2 }; final byte[] v3 = new byte[] { 3 }; assertNull(htree.lookupFirst(k1)); assertFalse(htree.contains(k1)); assertNull(htree.insert(k1, v2)); assertEquals(htree.lookupFirst(k1), v2); assertTrue(htree.contains(k1)); assertNull(htree.insert(k1, v3)); final long addrCheckpoint1 = htree.writeCheckpoint(); htree = HTree.load(store, addrCheckpoint1, true/* readOnly */); assertEquals(htree.lookupFirst(k1), v3); assertTrue(htree.contains(k1)); final ITupleIterator<?> iter = htree.lookupAll(k1); int count = 0; while (iter.hasNext()) { iter.next(); count++; } assertEquals(2, count); } finally { store.destroy(); } } public void test_duplicateKeyRangeScans_3bits_500dups() { doTest(3, 500); } public void test_duplicateKeyRangeScans_4bits_500dups() { doTest(4, 500); } public void test_duplicateKeyRangeScans_10bits_500dups() { doTest(10, 500); } public void test_duplicateKeyRangeScans_3bits_5000dups() { doTest(3, 5000); } public void test_duplicateKeyRangeScans_4bits_5000dups() { doTest(4, 5000); } public void test_duplicateKeyRangeScans_10bits_5000dups() { doTest(10, 5000); } /** * Test helper for a test based on duplicate keys. The test is designed to * verify that the keys are stored correctly and that a scan of the records * having that key returns all entries stored under that key. * * @param addressBits * The #of address bits. * @param ndups * The #of duplicate entries for a given key. */ private void doTest(final int addressBits, final int ndups) { final IRawStore store = new SimpleMemoryRawStore(); // final IRawStore store = getRWStore(); try { HTree htree = getHTree(store, addressBits, false/* rawRecords */, true/* persistent */); final byte[] k1 = new byte[] { 1,2,3,4 }; final byte[] k2 = new byte[] { 3,6,4 }; final byte[] k3 = new byte[] { 7,8,5,6 }; final byte[] v1 = new byte[] { 1 }; final byte[] v2 = new byte[] { 2 }; final byte[] v3 = new byte[] { 3 }; try { for (int i = 0; i < ndups; i++) { assertNull(htree.insert(k1, v1)); assertNull(htree.insert(k2, v2)); assertNull(htree.insert(k3, v3)); // This checks boundary insert conditions on the mutable buffers final String failure = "After inserting: " + (i+1); assertTrue(failure, htree.contains(k1)); // 2000 dups fails here with 10 bit depth, 70 dups with 6 bit assertTrue(failure, htree.contains(k2)); assertTrue(failure, htree.contains(k3)); } } finally { // htree.dump(Level.DEBUG, System.out, true/* materialize */); } assertTrue(htree.contains(k1)); // 2000 dups fails here with 10 bit depth, 70 dups with 6 bit assertTrue(htree.contains(k2)); assertTrue(htree.contains(k3)); // Check first with MutableBuckets assertTrue(getCount(htree, k1) == ndups); assertTrue(getCount(htree, k2) == ndups); assertTrue(getCount(htree, k3) == ndups); final long addrCheckpoint1 = htree.writeCheckpoint(); htree = HTree.load(store, addrCheckpoint1, true/* readOnly */); final String failString1 = "addressBits: " + addressBits + ", dups: " + ndups; assertTrue(failString1, htree.contains(k1)); assertTrue(failString1, htree.contains(k2)); assertTrue(failString1, htree.contains(k3)); // htree.dump(Level.DEBUG, System.out, true/* materialize */); final String failString = failString1 + " != "; assertTrue(failString + getCount(htree, k1), getCount(htree, k1) == ndups); assertTrue(failString + getCount(htree, k2), getCount(htree, k2) == ndups); // 50 dups fails with 8bit depth, 5 dups with 6 bit depth assertTrue(failString + getCount(htree, k3), getCount(htree, k3) == ndups); // 500 dups fails here with 10bit depth } finally { store.destroy(); } } // private IRawStore getRWStore() { // // final Properties properties = getProperties(); // // properties.setProperty(Options.CREATE_TEMP_FILE, "true"); // // properties.setProperty(Options.DELETE_ON_EXIT, "true"); // // properties.setProperty(Options.BUFFER_MODE, BufferMode.DiskRW.toString()); // // properties.setProperty(Options.WRITE_CACHE_ENABLED, "" + true); // // return new Journal(properties);//.getBufferStrategy(); // // } /** * Return the #of entries having the specified key. * * @param htree * The index. * @param k1 * The key. * @return */ private int getCount(final HTree htree, final byte[] k1) { final ITupleIterator<?> iter = htree.lookupAll(k1); int count = 0; while (iter.hasNext()) { iter.next(); count++; } return count; } /** * Simple test where the keys and the values are dups. * * @throws IOException * @throws Exception */ public void test_duplicateKeyValues() throws IOException, Exception { final IRawStore store = new SimpleMemoryRawStore(); try { HTree htree = getHTree(store, 3/* addressBits */, false/* rawRecord */, true/* persistent */); final byte[] k1 = new byte[] { 1 }; final byte[] v2 = new byte[] { 2 }; assertNull(htree.lookupFirst(k1)); assertFalse(htree.contains(k1)); assertNull(htree.insert(k1, v2)); assertEquals(htree.lookupFirst(k1), v2); assertTrue(htree.contains(k1)); assertNull(htree.insert(k1, v2)); // before checkpoint. assertEquals(2, getCount(htree, k1)); final long addrCheckpoint1 = htree.writeCheckpoint(); htree = HTree.load(store, addrCheckpoint1, true/* readOnly */); assertEquals(htree.lookupFirst(k1), v2); assertTrue(htree.contains(k1)); // after checkpoint. assertEquals(2, getCount(htree, k1)); } finally { store.destroy(); } } }