/** 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 Apr 13, 2011 */ package com.bigdata.rdf.sail; import java.io.IOException; import java.util.Properties; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.log4j.Logger; import com.bigdata.ha.HAGlue; import com.bigdata.ha.QuorumService; import com.bigdata.journal.IIndexManager; import com.bigdata.journal.IJournal; import com.bigdata.journal.ITransactionService; import com.bigdata.journal.ITx; import com.bigdata.quorum.AsynchronousQuorumCloseException; import com.bigdata.quorum.Quorum; import com.bigdata.rdf.sail.BigdataSail.UnisolatedCallable; import com.bigdata.rdf.store.AbstractTripleStore; import com.bigdata.rdf.store.LocalTripleStore; import com.bigdata.rdf.store.ScaleOutTripleStore; import com.bigdata.rdf.task.AbstractApiTask; import com.bigdata.util.InnerCause; /** * Task creates a KB for the given namespace iff no such KB exists. The correct * use of this class is as follows: * * <pre> * AbstractApiTask.submitApiTask(indexManager, new CreateKBTask(namespace, properties)).get(); * </pre> * * @see DestroyKBTask * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> */ public class CreateKBTask extends AbstractApiTask<Void> { private static final transient Logger log = Logger.getLogger(CreateKBTask.class); private static final transient Logger txLog = Logger.getLogger("com.bigdata.txLog"); /** * The effective properties that will be used to create the namespace. */ private final Properties properties; /** * Return the effective properties that will be used to create the namespace. */ protected Properties getProperties() { return properties; } public CreateKBTask(final String namespace, final Properties properties) { super(namespace, ITx.UNISOLATED, true/* isGRSRequired */); if (properties == null) throw new IllegalArgumentException(); // Use the caller's properties as the default. this.properties = new Properties(properties); // override the namespace. this.properties.setProperty(BigdataSail.Options.NAMESPACE, namespace); } @Override final public boolean isReadOnly() { return false; } @Override public Void call() throws Exception { try { doRun(); } catch (Throwable t) { if (InnerCause.isInnerCause(t, AsynchronousQuorumCloseException.class)) { /* * The quorum is closed, so we stopped trying. * * Note: This can also happen if the quorum has not been started * yet. The HAJournalServer explicitly invokes the CreateKBTask * when entering "RunMet" in order to handle this case. */ log.warn(t); } else { log.error(t, t); } throw new Exception(t); } return null; } /** * Note: This process is not robust if the leader is elected and becomes * HAReady and then fails over before the KB is created. The task should be * re-submitted by the new leader once that leader is elected. * * @throws Exception */ private void doRun() throws Exception { final IIndexManager indexManager = getIndexManager(); if (indexManager instanceof IJournal) { /* * Create a local triple store. * * Note: This hands over the logic to some custom code located * on the BigdataSail. */ final IJournal jnl = (IJournal) indexManager; final Quorum<HAGlue, QuorumService<HAGlue>> quorum = jnl .getQuorum(); boolean isSoloOrLeader; if (quorum == null) { isSoloOrLeader = true; } else { /* * Wait for a quorum meet. */ final long token; try { long tmp = quorum.token(); if (tmp == Quorum.NO_QUORUM) { // Only log if we are going to wait. log.warn("Awaiting quorum."); tmp = quorum.awaitQuorum(); } token = tmp; assert token != Quorum.NO_QUORUM; } catch (AsynchronousQuorumCloseException e1) { throw new RuntimeException(e1); } catch (InterruptedException e1) { throw new RuntimeException(e1); } /* * Now wait until the service is HAReady. */ try { jnl.awaitHAReady(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (AsynchronousQuorumCloseException e) { throw new RuntimeException(e); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (TimeoutException e) { throw new RuntimeException(e); } if (quorum.getMember().isLeader(token)) { isSoloOrLeader = true; } else { isSoloOrLeader = false; } final IJournal journal = jnl; if (journal.isGroupCommit() && journal.getRootBlockView().getCommitCounter() == 0L) { /* * Force the GRS to be materialized. This is necessary for the * initial KB create when using group commit and HA. (For HA the * initial KB create is single threaded within the context of the * leader election. However, this is not true for a standalone * Journal.) * * Note: This logic will fail if AbstractTask uses a * DefaultResourceLocator that is based on the HAJournal and not * on an IsolatedActionJournal because that will allow the * GlobalRowStoreHelper.getGlobalRowStore() method to registerr * the GSR index on the unisolated Name2Addr rather than the n2a * class inside of the AbstractTask. */ journal.getGlobalRowStore(); journal.commit(); } } if (isSoloOrLeader) { /* * Note: createLTS() writes on the GRS. This is an atomic row * store. The change is automatically committed. The unisolated * view of the BigdataSailConnection is being used solely to * have the correct locks. * * @see BLZG-2023, BLZG-2041 */ // Wrap with SAIL. final BigdataSail sail = new BigdataSail(namespace, getIndexManager()); try { sail.initialize(); final UnisolatedCallable<Void> task = new UnisolatedCallable<Void>() { @Override public Void call() throws Exception { if (!sail.exists()) { // create the appropriate as configured triple/quad store. createLTS(jnl, getProperties()); if (txLog.isInfoEnabled()) txLog.info("SAIL-CREATE-NAMESPACE: namespace=" + namespace); // FIXME BLZG-2041 document on wiki } return null; } }; try { // Do work with appropriate locks are held. sail.getUnisolatedConnectionLocksAndRunLambda(task); } finally { // Release locks. task.releaseLocks(); } } finally { sail.shutDown(); } } } else { // Attempt to resolve the namespace. if (indexManager.getResourceLocator().locate(namespace, ITx.UNISOLATED) == null) { /* * Register triple store for scale-out. * * Note: Scale-out does not have a global lock. */ if (log.isInfoEnabled()) log.info("Creating KB instance: namespace=" + namespace); final ScaleOutTripleStore lts = new ScaleOutTripleStore( indexManager, namespace, ITx.UNISOLATED, getProperties()); // Note: Commit in scale-out is shard-wise acid. lts.create(); if (txLog.isInfoEnabled()) txLog.info("CREATE: namespace=" + namespace); } // if( tripleStore == null ) } } /** * Create an {@link AbstractTripleStore} instance against a local database. * <p> * Note: For group commit, the caller will be holding the resource lock for * the namespace. * * @param indexManager * @param properties * * @throws IOException */ private void createLTS(final IJournal indexManager, final Properties properties) throws IOException { final String namespace = properties.getProperty( BigdataSail.Options.NAMESPACE, BigdataSail.Options.DEFAULT_NAMESPACE); // throws an exception if there are inconsistent properties BigdataSail.checkProperties(properties); // /** // * Note: Unless group commit is enabled, we need to make this operation // * mutually exclusive with KB level writers in order to avoid the // * possibility of a triggering a commit during the middle of a // * BigdataSailConnection level operation (or visa versa). // * // * Note: When group commit is not enabled, the indexManager will be a // * Journal class. When it is enabled, it will merely implement the // * IJournal interface. // * // * @see #1143 (Isolation broken in NSS when groupCommit disabled) // */ // final boolean isGroupCommit = indexManager.isGroupCommit(); // boolean acquiredConnection = false; // try { // // if (!isGroupCommit) { // try { // // acquire the unisolated connection permit. // ((Journal) indexManager).acquireUnisolatedConnection(); // acquiredConnection = true; // } catch (InterruptedException e) { // throw new RuntimeException(e); // } // } // Note: Already checked by the caller. // // // Check for pre-existing instance. // { // // final LocalTripleStore lts = (LocalTripleStore) indexManager // .getResourceLocator().locate(namespace, ITx.UNISOLATED); // // if (lts != null) { // // return; // // } // // } // Create a new instance. { if (Boolean.parseBoolean(properties.getProperty( BigdataSail.Options.ISOLATABLE_INDICES, BigdataSail.Options.DEFAULT_ISOLATABLE_INDICES))) { /* * Isolatable indices: requires the use of a tx to create the KB * instance. * * FIXME BLZG-2041: Verify test coverage of this code path. */ final ITransactionService txService = indexManager .getLocalTransactionManager().getTransactionService(); final long txIdCreate = txService.newTx(ITx.UNISOLATED); boolean ok = false; try { final AbstractTripleStore txCreateView = new LocalTripleStore( indexManager, namespace, Long.valueOf(txIdCreate), properties); // create the kb instance within the tx. txCreateView.create(); // commit the tx. txService.commit(txIdCreate); ok = true; } finally { if (!ok) txService.abort(txIdCreate); } } else { /* * Create KB without isolatable indices. */ final LocalTripleStore lts = new LocalTripleStore(indexManager, namespace, ITx.UNISOLATED, properties); lts.create(); } } // /* // * Now that we have created the instance, either using a tx or the // * unisolated connection, locate the triple store resource and return // * it. // */ // { // // final LocalTripleStore lts = (LocalTripleStore) indexManager // .getResourceLocator().locate(namespace, ITx.UNISOLATED); // // if (lts == null) { // // /* // * This should only occur if there is a concurrent destroy, which // * is highly unlikely to say the least. // */ // throw new RuntimeException("Concurrent create/destroy: " // + namespace); // // } // // return; // // } // } catch (IOException ex) { // // throw new RuntimeException(ex); // // } finally { // // if (!isGroupCommit && acquiredConnection) { // // /** // * When group commit is not enabled, we need to release the // * unisolated connection. // * // * @see #1143 (Isolation broken in NSS when groupCommit disabled) // */ // ((Journal) indexManager).releaseUnisolatedConnection(); // // } // // } } }