/*
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");
}
}