/* 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 Feb 14, 2008 */ package com.bigdata.btree.isolation; import java.io.IOException; import java.util.Iterator; import java.util.Properties; import java.util.UUID; import com.bigdata.btree.AbstractBTree; import com.bigdata.btree.AbstractBTreeTestCase; import com.bigdata.btree.BTree; import com.bigdata.btree.IRangeQuery; import com.bigdata.btree.ITuple; import com.bigdata.btree.ITupleIterator; import com.bigdata.btree.IndexMetadata; import com.bigdata.btree.isolation.IsolatedFusedView; import com.bigdata.btree.view.FusedView; import com.bigdata.btree.view.TestFusedView; import com.bigdata.journal.BufferMode; import com.bigdata.journal.Journal; import com.bigdata.journal.Options; import com.bigdata.util.BytesUtil; /** * Test suite for {@link IsolatedFusedView}. * <p> * Note: the test suite for an isolated write set is similar to a * {@link FusedView} except that it must also handle the per tuple revision * timestamps properly. The timestamps do not really effect the iterator (other * than {@link Iterator#remove()} or various lookup methods - they mainly * interact with the logic for detecting write-write conflicts. * * @see TestFusedView * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class TestIsolatedFusedView extends AbstractBTreeTestCase { public TestIsolatedFusedView() { } public TestIsolatedFusedView(String name) { super(name); } /** * Unit test examines the propagation of timestamps from the ground state * into the isolated write set with both insert() and remove() operations. * The test does not explore validation of the write set or mergeDown onto * the unisolated index (aka the commit of the transaction). * * @throws IOException */ public void test_writeSetIsolation() throws IOException { final byte[] k3 = i2k(3); final byte[] k5 = i2k(5); final byte[] k7 = i2k(7); final byte[] v3a = new byte[]{3}; final byte[] v5a = new byte[]{5}; final byte[] v7a = new byte[]{7}; final byte[] v3b = new byte[]{3,1}; final byte[] v5b = new byte[]{5,1}; final byte[] v7b = new byte[]{7,1}; final Properties properties = new Properties(); properties.setProperty(Options.BUFFER_MODE, BufferMode.Transient.toString()); final Journal journal = new Journal(properties); try { // two btrees with the same index UUID. final IndexMetadata md = new IndexMetadata(UUID.randomUUID()); { md.setBranchingFactor(3); md.setDeleteMarkers(true); md.setVersionTimestamps(true); } /* * Some distinct timestamps. */ final long t1 = journal.nextTimestamp(); final long t2 = journal.nextTimestamp(); final long t3 = journal.nextTimestamp(); final long startTime = journal.nextTimestamp(); final long t4 = journal.nextTimestamp(); /* * Create an index, write some data on that index, and checkpoint the * index to obtain the ground state against which the transaction will * run. */ final BTree unisolatedIndex = BTree.create(journal, md); unisolatedIndex.insert(k3, v3a, false/* deleted */, false/*putIfAbsent*/, t1, null/* tuple */); unisolatedIndex.insert(k7, null, true/* deleted */, false/*putIfAbsent*/, t2, null/* tuple */); // checkpoint the unisolated index. final long addrCheckpoint1 = unisolatedIndex.writeCheckpoint(); // load the ground state from that checkpoint. final BTree groundState = BTree.load(journal, addrCheckpoint1, false/* readOnly */); /* * Create the isolated write set. Keys found in the isolated write set * will cause the search to halt. If the key is not in the isolated * write set then the groundState will also be searched. A miss is * reported if the key is not found in the transaction's view of the * index. * * Note: Since delete markers are enabled keys will be recognized when * the index entry has been marked as deleted. */ // create the transaction's write set. final BTree writeSet = BTree.create(journal, md.clone()); // create the transaction's isolated view. final IsolatedFusedView view = new IsolatedFusedView(startTime, new AbstractBTree[] { writeSet, groundState }); assertTrue(view.contains(k3)); assertFalse(view.contains(k7)); assertSameIterator(new byte[][]{v3a}, view.rangeIterator(null, null)); { /* * Verify all entries, included those that are marked as deleted. */ final ITupleIterator itr = view .rangeIterator(null, null, 0/* capacity */, IRangeQuery.DEFAULT | IRangeQuery.DELETED, null/* filter */); // k3 assertTrue(itr.hasNext()); ITuple tuple; tuple = itr.next(); assertEquals(k3,tuple.getKey()); assertEquals(v3a,tuple.getValue()); assertFalse(tuple.isDeletedVersion()); assertEquals(t1,tuple.getVersionTimestamp()); // k7 (deleted) assertTrue(itr.hasNext()); tuple = itr.next(); assertEquals(k7,tuple.getKey()); assertTrue(tuple.isDeletedVersion()); assertEquals(t2,tuple.getVersionTimestamp()); } /* * Write on the isolated view under a new key (k5). */ assertEquals(null,view.insert(k5, v5a)); assertEquals(v5a,view.lookup(k5));// in view assertEquals(v5a,writeSet.lookup(k5)); // on write set. assertFalse(groundState.contains(k5)); // not on ground state. { /* * Verify the written entry. */ ITuple tuple = view.rangeIterator(k5, BytesUtil.successor(k5)).next(); assertEquals(k5,tuple.getKey()); assertEquals(v5a,tuple.getValue()); assertFalse(tuple.isDeletedVersion()); assertEquals(startTime,tuple.getVersionTimestamp()); } /* * Write on the isolated view under an existing key (k3). */ assertEquals(v3a,view.insert(k3, v3b)); assertEquals(v3b,view.lookup(k3));// in view assertEquals(v3b,writeSet.lookup(k3)); // on write set. assertEquals(v3a,groundState.lookup(k3)); // unchanged on groundState. { /* * Verify the written entry. */ ITuple tuple = view.rangeIterator(k3, BytesUtil.successor(k3)).next(); assertEquals(k3,tuple.getKey()); assertEquals(v3b,tuple.getValue()); assertFalse(tuple.isDeletedVersion()); assertEquals(t1,tuple.getVersionTimestamp()); // note: timestamp is copied from groundState! } /* * Write on the isolated view under an existing key with a deleted entry (k7). */ assertEquals(null,view.insert(k7, v7a)); assertEquals(v7a,view.lookup(k7));// in view assertEquals(v7a,writeSet.lookup(k7)); // on write set. assertEquals(null,groundState.lookup(k7)); // unchanged on groundState. { /* * Verify the written entry. */ ITuple tuple = view.rangeIterator(k7, BytesUtil.successor(k7)).next(); assertEquals(k7,tuple.getKey()); assertEquals(v7a,tuple.getValue()); assertFalse(tuple.isDeletedVersion()); assertEquals(t2,tuple.getVersionTimestamp()); // note: timestamp is copied from groundState! } /* * Verify re-write of k7 updates the value but not the timestamp. */ assertEquals(v7a,view.insert(k7, v7b)); assertEquals(v7b,view.lookup(k7));// in view assertEquals(v7b,writeSet.lookup(k7)); // on write set. assertEquals(null,groundState.lookup(k7)); // unchanged on groundState. { /* * Verify the written entry. */ ITuple tuple = view.rangeIterator(k7, BytesUtil.successor(k7)).next(); assertEquals(k7,tuple.getKey()); assertEquals(v7b,tuple.getValue()); assertFalse(tuple.isDeletedVersion()); assertEquals(t2,tuple.getVersionTimestamp()); // note: timestamp unchanged from groundState! } } finally { journal.destroy(); } } /** * Unit test for validating a write set against the current ground state. * ground state and are not detected by the isolated view. when those writes * on are the same keys as writes by the transaction a write-write conflict * will result. When they are on different keys there will be no conflict. * * @todo write validation tests. * * @todo write tests that correctly detect write-write conflicts. pay * attention to write-delete, delete-delete, and delete-write cases as * well. * * @todo verify that writes on the unisolated index are not visible in the * * @todo write on some keys that are not used by the tx to verify absence of * write-write conflict. * * @todo verify commit */ public void test_validate() { // fail("write test"); } /** * Unit test for merging down a validated write set onto the current ground * state. * * @todo write mergeDown tests. */ public void test_mergeDown() { // fail("write test"); } }