/**
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 Oct 10, 2007
*/
package com.bigdata.journal;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import com.bigdata.btree.IIndex;
import com.bigdata.btree.IndexMetadata;
import com.bigdata.journal.Journal.Options;
/**
* Test suite for {@link RegisterIndexTask} and {@link DropIndexTask}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestAddDropIndexTask extends ProxyTestCase {
/**
*
*/
public TestAddDropIndexTask() {
super();
}
/**
* @param arg0
*/
public TestAddDropIndexTask(String arg0) {
super(arg0);
}
/**
* Test ability to submit an unisolated write task that creates a named
* index and then shutdown the journal. The journal is then re-opened and we
* verify that the registered index is restart safe. Finally we drop the
* index, close the journal and then re-open it again to verify that the
* drop index operation was restart safe.
*/
public void test_addDropIndex() {
// Note: wrapped since we modify the properties during the test!!!
final Properties properties = new Properties(getProperties());
Journal journal = new Journal(properties);
try {
final String name = "abc";
final UUID indexUUID = UUID.randomUUID();
/*
* Run task to register a named index.
*/
{
final long commitCounterBefore = journal.getRootBlockView().getCommitCounter();
final Future<? extends Object> future = journal.submit(new RegisterIndexTask( journal,
name, new IndexMetadata(name, indexUUID)));
try {
log.info("Resolving future for task.");
assertEquals( "indexUUID", indexUUID, (UUID)future.get() );
log.info("Resolved future");
/*
* This verifies that the write task did not return control to
* the caller until the write set of that task was committed.
*/
assertEquals("commit counter unchanged?",
commitCounterBefore + 1, journal.getRootBlockView()
.getCommitCounter());
} catch (InterruptedException ex) {
fail("Not expecting: " + ex, ex);
} catch (ExecutionException ex) {
fail("Not expecting: " + ex, ex);
}
}
/*
* reopen (iff backed by stable store).
*/
if(journal.isStable()) {
journal.shutdown();
properties.setProperty(Options.CREATE_TEMP_FILE,"false");
properties.setProperty(Options.FILE,journal.getFile().toString());
journal = new Journal(properties);
}
/*
* Verify that the index was registered.
*/
{
final long commitCounterBefore = journal.getRootBlockView()
.getCommitCounter();
final Future<? extends Object> future = journal.submit(new AbstractTask( journal,
ITx.READ_COMMITTED, name) {
protected Object doTask() throws Exception {
/*
* Note: Throws an exception if the index is not registered.
*/
IIndex ndx = getIndex(name);
assertEquals("indexUUID", indexUUID, ndx.getIndexMetadata()
.getIndexUUID());
return null;
}
});
try {
// return value should be [null] for this task.
assertNull( future.get() );
/*
* The commit counter MUST NOT have been changed since we ran a
* read-only task.
*/
assertEquals("commit counter changed?",
commitCounterBefore, journal.getRootBlockView()
.getCommitCounter());
} catch (InterruptedException ex) {
fail("Not expecting: " + ex, ex);
} catch (ExecutionException ex) {
fail("Not expecting: " + ex, ex);
}
}
/*
* Drop the index.
*/
{
final long commitCounterBefore = journal.getRootBlockView().getCommitCounter();
final Future<? extends Object> future = journal.submit(new DropIndexTask(journal,
name));
try {
// return is true iff the index was pre-existing (and therefore
// was dropped).
assertTrue( "Index did not exist?", (Boolean)future.get() );
/*
* This verifies that the write task did not return control to
* the caller until the write set of that task was committed.
*/
assertEquals("commit counter unchanged?",
commitCounterBefore + 1, journal.getRootBlockView()
.getCommitCounter());
} catch (InterruptedException ex) {
fail("Not expecting: " + ex, ex);
} catch (ExecutionException ex) {
fail("Not expecting: " + ex, ex);
}
}
/*
* Re-open the journal (iff backed by stable store).
*/
if(journal.isStable()) {
journal.shutdown();
journal = new Journal(properties);
}
/*
* Verify that the index is gone.
*/
{
final long commitCounterBefore = journal.getRootBlockView().getCommitCounter();
final Future<? extends Object> future = journal.submit(new AbstractTask(journal,
ITx.READ_COMMITTED, name) {
protected Object doTask() throws Exception {
/*
* Note: Throws an exception if the index is not registered.
*/
try {
getIndex(name);
fail("Expecting: "+NoSuchIndexException.class);
} catch(NoSuchIndexException ex) {
System.err.println("Ignoring expected exception: "+ex);
}
return null;
}
});
try {
// return value should be [null] for this task.
assertNull( future.get() );
/*
* We ran a read-only task so the commit counter MUST NOT have
* been changed.
*/
assertEquals("commit counter unchanged?",
commitCounterBefore, journal.getRootBlockView()
.getCommitCounter());
} catch (InterruptedException ex) {
fail("Not expecting: " + ex, ex);
} catch (ExecutionException ex) {
fail("Not expecting: " + ex, ex);
}
}
} finally {
journal.destroy();
}
}
/**
* Test registers an index and then verifies that a second
* {@link RegisterIndexTask} will return <code>false</code> since the
* index already exists.
*
* @throws ExecutionException
* @throws InterruptedException
*/
public void test_addDropIndex_twice() throws InterruptedException, ExecutionException {
final Properties properties = new Properties(getProperties());
// properties.setProperty(Options.BUFFER_MODE,BufferMode.Disk.toString());
//
// properties.setProperty(Options.CREATE_TEMP_FILE,"true");
final Journal journal = new Journal(properties);
try {
final String name = "abc";
final UUID indexUUID = UUID.randomUUID();
/*
* Run task to register a named index.
*/
{
final long commitCounterBefore = journal.getRootBlockView()
.getCommitCounter();
final Future<? extends Object> future = journal.submit(new RegisterIndexTask(
journal, name, new IndexMetadata(name, indexUUID)));
assertEquals("indexUUID", indexUUID, (UUID) future.get());
/*
* This verifies that the write task did not return control to the
* caller until the write set of that task was committed.
*/
assertEquals("commit counter unchanged?", commitCounterBefore + 1,
journal.getRootBlockView().getCommitCounter());
// verify access to the index.
assertNotNull(journal.getIndex(name));
assertEquals(indexUUID, journal.getIndex(name).getIndexMetadata()
.getIndexUUID());
}
/*
* Run another task to re-register the same named index.
*/
{
final long commitCounterBefore = journal.getRootBlockView()
.getCommitCounter();
final Future<? extends Object> future = journal.submit(new RegisterIndexTask(
journal, name, new IndexMetadata(name, indexUUID)));
// Note: the UUID for the pre-existing index is returned.
assertEquals("indexUUID", indexUUID, (UUID) future.get());
/*
* This verifies that no commit was performed since no data was
* actually written on the store because the index was pre-existing.
*/
assertEquals("commit counter changed?", commitCounterBefore,
journal.getRootBlockView().getCommitCounter());
}
/*
* Now drop the index.
*/
{
final long commitCounterBefore = journal.getRootBlockView()
.getCommitCounter();
final Future<? extends Object> future = journal.submit(new DropIndexTask(journal,
name));
// should return true if the index was dropped.
assertTrue("Index did not exist?", (Boolean) future.get());
/*
* Verify that a commit was performed.
*/
assertEquals("commit counter unchanged?", commitCounterBefore + 1,
journal.getRootBlockView().getCommitCounter());
}
/*
* Now drop the index again.
*/
{
final long commitCounterBefore = journal.getRootBlockView()
.getCommitCounter();
final Future<? extends Object> future = journal.submit(new DropIndexTask(journal,
name));
// should return false since the index does not exist.
assertFalse("Index exists?", (Boolean) future.get());
/*
* Verify that a commit was NOT performed since no data was written
* by the task.
*/
assertEquals("commit counter changed?", commitCounterBefore,
journal.getRootBlockView().getCommitCounter());
}
} finally {
journal.destroy();
}
}
/**
* Test attempt operations against a new journal (nothing committed) and
* verify that we see {@link NoSuchIndexException}s rather than something
* odder. This is an edge case since {@link Journal#getCommitRecord()} will
* have 0L for all root addresses until the first commit - this means that
* the {@link AbstractJournal#_name2Addr} can not be loaded from the commit
* record!
*
* @throws InterruptedException
*/
public void test_NoSuchIndexException() throws InterruptedException {
final Journal journal = new Journal(getProperties());
try {
final String name = "abc";
final Future<? extends Object> future = journal.submit(new AbstractTask(journal,
ITx.READ_COMMITTED, name) {
protected Object doTask() throws Exception {
getIndex(name);
return null;
}});
try {
future.get();
fail("Expecting wrapped "+NoSuchIndexException.class);
} catch(ExecutionException ex) {
if(ex.getCause() instanceof NoSuchIndexException ) {
System.err.println("Ignoring expected exception: "+ex);
} else {
fail("Expecting wrapped "+NoSuchIndexException.class);
}
}
} finally {
journal.destroy();
}
}
}