/* 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 Jul 9, 2008 */ package com.bigdata.relation.locator; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.UUID; import junit.framework.TestCase2; import com.bigdata.bop.IBindingSet; import com.bigdata.bop.IPredicate; import com.bigdata.btree.IIndex; import com.bigdata.btree.IndexMetadata; import com.bigdata.journal.BufferMode; import com.bigdata.journal.IIndexManager; import com.bigdata.journal.ITx; import com.bigdata.journal.Journal; import com.bigdata.journal.Journal.Options; import com.bigdata.rdf.spo.ISPO; import com.bigdata.relation.AbstractRelation; import com.bigdata.relation.AbstractResource; import com.bigdata.service.IBigdataFederation; import com.bigdata.striterator.IChunkedOrderedIterator; import com.bigdata.striterator.IKeyOrder; import com.bigdata.util.NT; /** * Test suite for location relations, etc. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ * * @todo make this a proxy test case and run for {@link Journal} and * {@link IBigdataFederation} */ public class TestDefaultResourceLocator extends TestCase2 { public TestDefaultResourceLocator() { super(); } public TestDefaultResourceLocator(String name) { super(name); } public Properties getProperties() { Properties properties = super.getProperties(); properties.setProperty(Options.BUFFER_MODE, BufferMode.Transient .toString()); return properties; } /** * @todo locate a relation. * * @todo locate a relation, write on it, verify read-committed view not yet * visible, then commit and verify the read committed view is visible. * * @todo same as above, but the relation is on a temporary store that is * added to the set of index managers against which relations are * resolved. * */ public void test_locateRelation() { final Properties properties = getProperties(); final Journal store = new Journal( properties ); final String namespace = "test"; try { // instantiate relation. MockRelation mockRelation = new MockRelation(store, namespace, ITx.UNISOLATED, properties); /* * the index for the relation does not exist yet. we verify * that, and then create the index. */ assertNull(mockRelation.getIndex()); // create indices. mockRelation.create(); assertNotNull(mockRelation.getIndex()); { /* * Should be locatable now since the writes on the global row * store are unisolated (ah!, but only if you are using the * concurrency control API) */ assertNotNull(store.getResourceLocator().locate(namespace, ITx.UNISOLATED)); // a request for the unisolated view gives us the same instance. assertTrue(((MockRelation) store.getResourceLocator().locate( namespace, ITx.UNISOLATED)) == mockRelation); /* * the read-committed relation is also locatable since its in * the global row store but it will not see the indices until * (a) they have been created; and (b) there has been a commit * of the journal. */ assertNotNull(store.getResourceLocator().locate(namespace, ITx.READ_COMMITTED)); // a request for the read committed view is not the same // instance as the unisolated view. assertTrue(((MockRelation) store.getResourceLocator().locate( namespace, ITx.READ_COMMITTED)) != mockRelation); } { // a request for the unisolated view shows that the index // exists. assertNotNull(((MockRelation) store.getResourceLocator() .locate(namespace, ITx.UNISOLATED)).getIndex()); // a request for the unisolated view gives us the same instance. assertTrue(((MockRelation) store.getResourceLocator().locate( namespace, ITx.UNISOLATED)) == mockRelation); /* * @todo The read-committed view still does not see the relation * since there has not been a commit yet after the index was * created. */ if(false) { assertNull(((MockRelation) store.getResourceLocator().locate( namespace, ITx.READ_COMMITTED))); final MockRelation readCommittedView1 = (MockRelation) store .getResourceLocator().locate(namespace, ITx.READ_COMMITTED); // same view before a commit. assertTrue(readCommittedView1 == (MockRelation) store .getResourceLocator().locate(namespace, ITx.READ_COMMITTED)); // commit store.commit(); /* * should be a new read-committed view * * FIXME cache must be defeated for read-committed!!! at least * if there HAS been a commit */ final MockRelation readCommittedView2 = (MockRelation) store .getResourceLocator().locate(namespace, ITx.READ_COMMITTED); // different view after a commit. assertTrue(readCommittedView1 != readCommittedView2); /* * The index is now visible to the read committed view. */ assertNull(readCommittedView2.getIndex()); // still not visible to the old view. assertNull(readCommittedView1.getIndex()); // another request gives us the same view assertTrue(readCommittedView2 == (MockRelation) store .getResourceLocator().locate(namespace, ITx.READ_COMMITTED)); } } } finally { store.destroy(); } } /** * Unit test for property caching for locatable resources. */ public void test_propertyCache() { final Properties properties = getProperties(); final Journal store = new Journal( properties ); final String namespace = "test"; try { // write a small record onto the journal and force a commit. { final ByteBuffer b = ByteBuffer.allocate(4); b.putInt(0); b.flip(); store.write(b); assertNotSame(0L,store.commit()); } // verify resource can not be located yet. { // resource does not exist in UNISOLATED view. assertNull(store.getResourceLocator().locate(namespace, ITx.UNISOLATED)); // resource does not exist at lastCommitTime. assertNull(store.getResourceLocator().locate(namespace, store.getLastCommitTime())); // resource does not exist at read-only tx. { final long tx = store.newTx(ITx.READ_COMMITTED); try { assertNull(store.getResourceLocator().locate(namespace, store.getLastCommitTime())); } finally { store.abort(tx); } } } // instantiate relation. MockRelation mockRelation = new MockRelation(store, namespace, ITx.UNISOLATED, properties); // verify resource still can not be located. { // resource does not exist in UNISOLATED view. assertNull(store.getResourceLocator().locate(namespace, ITx.UNISOLATED)); // resource does not exist at lastCommitTime. assertNull(store.getResourceLocator().locate(namespace, store.getLastCommitTime())); // resource does not exist at read-only tx. { final long tx = store.newTx(ITx.READ_COMMITTED); try { assertNull(store.getResourceLocator().locate(namespace, store.getLastCommitTime())); } finally { store.abort(tx); } } } // create the resource, which writes the properties into the GRS. mockRelation.create(); /* */ { /* * The UNISOLATED view of the resource should be locatable now * since the writes on the global row store are unisolated. */ assertNotNull(store.getResourceLocator().locate(namespace, ITx.UNISOLATED)); // a request for the unisolated view gives us the same instance. assertTrue(store.getResourceLocator().locate(namespace, ITx.UNISOLATED) == mockRelation); /* * Note: The read-committed view is not, in fact, locatable. * This is because the GRS update on the Journal does not * include a commit. That update will become visible only * once we do a Journal.commit(). */ assertNull(store.getResourceLocator().locate(namespace, ITx.READ_COMMITTED)); // /* // * The read-committed view of the resource is also locatable. // */ // assertNotNull(store.getResourceLocator().locate(namespace, // ITx.READ_COMMITTED)); // // /* // * The read committed view is not the same instance as the // * unisolated view. // */ // assertTrue(((MockRelation) store.getResourceLocator().locate( // namespace, ITx.READ_COMMITTED)) != mockRelation); } // commit time immediately proceeding this commit. final long priorCommitTime = store.getLastCommitTime(); // commit, noting the commit time. final long lastCommitTime = store.commit(); { /* * The read-committed view of the resource is also locatable. */ assertNotNull(store.getResourceLocator().locate(namespace, ITx.READ_COMMITTED)); /* * The read committed view is not the same instance as the * unisolated view. */ assertTrue(((MockRelation) store.getResourceLocator().locate( namespace, ITx.READ_COMMITTED)) != mockRelation); } if(log.isInfoEnabled()) { log.info("priorCommitTime=" + priorCommitTime); log.info("lastCommitTime =" + lastCommitTime); } /* * Now create a few transactions against the newly created resource * and verify that the views returned for those transactions are * distinct, but that they share the same set of default Properties * (e.g., the propertyCache is working). * * @todo also test a read-historical read ! */ final long tx1 = store.newTx(store.getLastCommitTime()); // read-only tx final long tx2 = store.newTx(store.getLastCommitTime()); // read-only tx final long ts1 = store.getLastCommitTime() - 1; // historical read try { assertTrue(tx1 != tx2); assertTrue(ts1 != tx1); assertTrue(ts1 != tx2); /* * @todo There might not be enough commit latency to have * lastCommitTime - 1 be GT priorCommitTime. If this happens * either resolve issue 145 or add some latency into the test. * * @see http://sourceforge.net/apps/trac/bigdata/ticket/145 */ assertTrue(ts1 > priorCommitTime); // unisolated view. final AbstractResource<?> view_un = (AbstractResource<?>) store .getResourceLocator().locate(namespace, ITx.UNISOLATED); assertNotNull(view_un); // tx1 view. final AbstractResource<?> view_tx1 = (AbstractResource<?>) store .getResourceLocator().locate(namespace, tx1); assertNotNull(view_tx1); // tx2 view. final AbstractResource<?> view_tx2 = (AbstractResource<?>) store .getResourceLocator().locate(namespace, tx2); assertNotNull(view_tx2); // all views are distinct. assertTrue(view_un != view_tx1); assertTrue(view_un != view_tx2); assertTrue(view_tx1 != view_tx2); // each view has its correct timestamp. assertEquals(ITx.UNISOLATED, view_un.getTimestamp()); assertEquals(tx1, view_tx1.getTimestamp()); assertEquals(tx2, view_tx2.getTimestamp()); /* * Read-only views report the commit time from which they were * materialized. */ assertEquals(null, ((MockRelation) view_un).getCommitTime()); assertEquals(Long.valueOf(lastCommitTime), ((MockRelation) view_tx1).getCommitTime()); assertEquals(Long.valueOf(lastCommitTime), ((MockRelation) view_tx2).getCommitTime()); // each view has its own Properties object. final Properties p_un = view_un.getProperties(); final Properties p_tx1 = view_tx1.getProperties(); final Properties p_tx2 = view_tx2.getProperties(); assertTrue(p_un != p_tx1); assertTrue(p_un != p_tx2); assertTrue(p_tx1 != p_tx2); /* * Verify that the [propertyCache] is working. * * Note: Unfortunately, I have not been able to devise any means * of testing the [propertyCache] without exposing that as a * package private object. */ final DefaultResourceLocator<?> locator = (DefaultResourceLocator<?>) store .getResourceLocator(); // Not cached for the UNISOLATED view (mutable views can not be // cached). assertNull(locator.propertyCache.get(new NT(namespace, ITx.UNISOLATED))); // if (true) { // final Iterator<Map.Entry<NT, WeakReference<Map<String, Object>>>> itr = locator.propertyCache // .entryIterator(); // while (itr.hasNext()) { // final Map.Entry<NT, WeakReference<Map<String, Object>>> e = itr // .next(); // System.err.println(e.getKey() + " => " // + e.getValue().get()); // } // } // Not cached for the actual tx ids or read-only timestamp. assertNull(locator.propertyCache.get(new NT(namespace,tx1))); assertNull(locator.propertyCache.get(new NT(namespace,tx2))); assertNull(locator.propertyCache.get(new NT(namespace,ts1))); /* * Cached for the last commit time, which should have been used * to hand back the Properties for {tx1, tx2, ts1}. */ assertNotNull(locator.propertyCache.get(new NT(namespace, lastCommitTime))); // nothing for the prior commit time. assertNull(locator.propertyCache.get(new NT(namespace, priorCommitTime))); } finally { store.abort(tx1); store.abort(tx2); } } finally { store.destroy(); } } @SuppressWarnings("unchecked") private static class MockRelation extends AbstractRelation { static final private String indexName = "foo"; private IIndex ndx; /** * Exposed to the unit tests. */ @Override public Long getCommitTime() { return super.getCommitTime(); } /** * @param indexManager * @param namespace * @param timestamp * @param properties */ public MockRelation(IIndexManager indexManager, String namespace, Long timestamp, Properties properties) { super(indexManager, namespace, timestamp, properties); } public void create() { super.create(); if (getIndex() == null) { getIndexManager().registerIndex( new IndexMetadata(getNamespace() + indexName, UUID .randomUUID())); log.info("Created index."); getIndex(); } } public void destroy() { super.destroy(); if (getIndex() != null) { getIndexManager().dropIndex(getNamespace() + indexName); log.info("Dropped index."); } } private IIndex getIndex() { if (ndx == null) { ndx = getIndex(getNamespace() + indexName); } if (ndx == null) { log.info("Index not found."); } return ndx; } @Override public String getFQN(IKeyOrder keyOrder) { return null; } public long delete(IChunkedOrderedIterator itr) { return 0; } public long insert(IChunkedOrderedIterator itr) { return 0; } public Set getIndexNames() { return null; } public IKeyOrder getPrimaryKeyOrder() { return null; } public Iterator getKeyOrders() { return null; } public IKeyOrder getKeyOrder(IPredicate p) { return null; } public Object newElement(List a, IBindingSet bindingSet) { return null; } public Class<ISPO> getElementClass() { return null; } } // class MockRelation }