com.bigdata.journal.TemporaryStore Maven / Gradle / Ivy
/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
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 21, 2007
*/
package com.bigdata.journal;
import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import com.bigdata.bfs.BigdataFileSystem;
import com.bigdata.bfs.GlobalFileSystemHelper;
import com.bigdata.btree.BTree;
import com.bigdata.btree.Checkpoint;
import com.bigdata.btree.ICheckpointProtocol;
import com.bigdata.btree.IndexMetadata;
import com.bigdata.journal.Name2Addr.Entry;
import com.bigdata.rawstore.IRawStore;
import com.bigdata.rawstore.WormAddressManager;
import com.bigdata.relation.locator.DefaultResourceLocator;
import com.bigdata.sparse.GlobalRowStoreHelper;
import com.bigdata.sparse.SparseRowStore;
import com.bigdata.util.DaemonThreadFactory;
/**
* A temporary store that supports named indices but no concurrency controls.
*
* If you want a temporary store that supports named indices and concurrency
* controls then choose a {@link Journal} with {@link BufferMode#Temporary}.
* This has the advantage of full concurrency support, group commit, and
* low-latency startup (since the file is not created until the store attempts
* to write through to the disk). However, {@link TemporaryStore} is lighter
* weight precisely because it does not provide concurrency control.
*
* @author Bryan Thompson
*
* @see GIST
*/
//* {@link #checkpoint()} may be used to checkpoint the indices and
//* {@link #restoreLastCheckpoint()} may be used to revert to the last
//* checkpoint. If you note the checkpoint addresses from {@link #checkpoint()}
//* then you can restore any checkpoint with {@link #restoreCheckpoint(long)}
public class TemporaryStore extends TemporaryRawStore implements IBTreeManager {
private static final Logger log = Logger.getLogger(TemporaryStore.class);
/**
* The size of the live index cache for the {@link Name2Addr} instance.
*
* @todo this should be a configuration property once the temporary store
* accepts a {@link Properties} object in its ctor.
*
* @see Options#DEFAULT_LIVE_INDEX_CACHE_CAPACITY
*/
private final int liveIndexCacheCapacity = 20;
/**
* The timeout in milliseconds for stale entries in the live index cache for
* the {@link Name2Addr} instance.
*
* @todo this should be a configuration property once the temporary store
* accepts a {@link Properties} object in its ctor.
*
* @see Options#DEFAULT_LIVE_INDEX_CACHE_TIMEOUT
*/
private final long liveIndexCacheTimeout = Long
.parseLong(Options.DEFAULT_LIVE_INDEX_CACHE_TIMEOUT);
/**
* BTree mapping index names to the last metadata record committed for the
* named index. The keys are index names (unicode strings). The values are
* the last known address of the named btree.
*
* Note: This is a mutable {@link BTree} so it is NOT thread-safe. We always
* synchronize on this object before accessing it.
*/
private final Name2Addr name2Addr;
/**
* A {@link TemporaryStore} that can scale-up. The backing file will be
* created using the Java temporary file mechanism.
*
* @see WormAddressManager#SCALE_UP_OFFSET_BITS
* @see #getTempFile()
*/
public TemporaryStore() {
this(WormAddressManager.SCALE_UP_OFFSET_BITS);
}
/**
* A {@link TemporaryStore} provisioned with the specified offsetBits.
* The backing file will be created using the Java temporary file mechanism.
*
* @param offsetBits
* This determines the capacity of the store file and the maximum
* length of a record. The value is passed through to
* {@link WormAddressManager#WormAddressManager(int)}.
*/
public TemporaryStore(final int offsetBits) {
this(offsetBits, getTempFile());
}
/**
* A {@link TemporaryStore} provisioned with the specified offsetBits
* and backed by the specified file.
*
* @param offsetBits
* This determines the capacity of the store file and the maximum
* length of a record. The value is passed through to
* {@link WormAddressManager#WormAddressManager(int)}.
* @param file
* The backing file (may exist, but must be empty if it exists).
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public TemporaryStore(final int offsetBits, final File file) {
super(0L/* maximumExtent */, offsetBits, file);
name2Addr = Name2Addr.create(this);
name2Addr.setupCache(liveIndexCacheCapacity, liveIndexCacheTimeout);
executorService = Executors.newCachedThreadPool(new DaemonThreadFactory
(getClass().getName()+".executorService"));
resourceLocator = new DefaultResourceLocator(//
this,//
null // delegate
);
}
// /**
// * Setup the btree that resolved named btrees.
// */
// private void setupName2AddrBTree() {
//
// assert name2Addr == null;
//
// name2Addr = Name2Addr.create(this);
//
// name2Addr.setupCache(liveIndexCacheCapacity, liveIndexCacheTimeout);
//
// }
// /**
// * The address of the last checkpoint written. When ZERO(0L) no checkpoint
// * has been written and {@link #name2Addr} is simple discarded on
// * {@link #abort()}.
// */
// private long lastCheckpointAddr = 0L;
//
// /**
// * Reverts to the last checkpoint, if any. If there is no last checkpoint,
// * then the post-condition is as if the store had never been written on
// * (except that the storage on the backing file is not reclaimed).
// */
// public void restoreLastCheckpoint() {
//
// restoreCheckpoint(lastCheckpointAddr);
//
// }
//
// /**
// * Reverts to the checkpoint associated with the given checkpointAddr.
// * When ZERO(0L), the post-condition is as if the store had never been
// * written on (except that the storage on the backing file is not
// * reclaimed). The checkpointAddr is noted as the current
// * {@link #restoreLastCheckpoint()} point.
// */
// public void restoreCheckpoint(final long checkpointAddr) {
//
// assertOpen();
//
// name2Addr = null;
//
// if (checkpointAddr != 0L) {
//
// name2Addr = (Name2Addr) Name2Addr
// .load(this, checkpointAddr, false/* readOnly */);
//
// } else {
//
// setupName2AddrBTree();
//
// }
//
// // note the restore point.
// lastCheckpointAddr = checkpointAddr;
//
// }
//
// /**
// * Checkpoints the dirty indices and notes the new
// * {@link #restoreLastCheckpoint()} point. You can revert to the last
// * written checkpoint using {@link #restoreLastCheckpoint()} or to an
// * arbitrary checkpoint using {@link #restoreCheckpoint(long)}.
// *
// * Note: {@link ITx#READ_COMMITTED} views of indices become available after
// * a {@link #checkpoint()}. If the store has not been checkpointed, then
// * the read committed views are unavailable for an index. After a checkpoint
// * in which a given index was dirty, a new read-committed view is available
// * for that index and checkpoint.
// *
// * Note: This is NOT an atomic commit protocol, but the restore point will
// * be updated iff the checkpoint succeeds.
// *
// * @return The checkpoint address.
// */
// public long checkpoint() {
//
// assertOpen();
//
// // checkpoint the indices and note the restore point.
// return lastCheckpointAddr = name2Addr.handleCommit(System
// .currentTimeMillis());
//
// }
@Override
public void registerIndex(final IndexMetadata metadata) {
registerIndex(metadata.getName(), metadata);
}
@Override
public BTree registerIndex(final String name, final IndexMetadata metadata) {
return (BTree) register(name, metadata);
}
/**
* Variant method creates and registered a named persistence capable data
* structure but does not assume that the data structure will be a
* {@link BTree}.
*
* @param store
* The backing store.
* @param metadata
* The metadata that describes the data structure to be created.
*
* @return The persistence capable data structure.
*
* @see Checkpoint#create(IRawStore, IndexMetadata)
*/
@Override
public ICheckpointProtocol register(final String name,
final IndexMetadata metadata) {
final ICheckpointProtocol ndx = Checkpoint.create(this, metadata);
register(name, ndx);
return ndx;
}
@Override
final public BTree registerIndex(final String name, final BTree btree) {
registerIndex(name, btree);
return btree;
}
/**
* Register a named persistence capable data structure (core impl).
*
* @param name
* The name.
* @param ndx
* The data structure.
*/
private final void register(final String name, final ICheckpointProtocol ndx) {
synchronized (name2Addr) {
assertOpen();
// add to the persistent name map.
name2Addr.registerIndex(name, ndx);
}
}
@Override
public void dropIndex(final String name) {
synchronized(name2Addr) {
assertOpen();
// drop from the persistent name map.
name2Addr.dropIndex(name);
}
}
@Override
public Iterator indexNameScan(final String prefix,
final long timestampIsIgnored) {
final List names = new LinkedList();
synchronized (name2Addr) {
final Iterator itr = Name2Addr.indexNameScan(prefix,
name2Addr);
while (itr.hasNext()) {
names.add(itr.next());
}
}
return names.iterator();
}
@Override
public ICheckpointProtocol getUnisolatedIndex(final String name) {
synchronized(name2Addr) {
assertOpen();
return name2Addr.getIndex(name);
}
}
// /**
// * Return an {@link ITx#UNISOLATED} view of the named index -or-
// * null
if there is no registered index by that name.
// */
@Override
public BTree getIndex(final String name) {
return (BTree) getUnisolatedIndex(name);
}
// /**
// * Return an {@link ITx#UNISOLATED} view of the named index -or-
// * null
if there is no registered index by that name.
// */
// public HTree getHTree(final String name) {
//
// return (HTree) getUnisolatedIndex(name);
//
// }
@Override
public BTree getIndex(final String name, final long timestamp) {
return (BTree) getIndexLocal(name, timestamp);
}
/**
* {@inheritDoc}
*
* Note: Requests for historical reads or read-only tx views will result
* return the {@link ITx#UNISOLATED} index view.
*
* Note: If {@link ITx#READ_COMMITTED} is requested, then the returned
* {@link BTree} will reflect the state of the named index as of the last
* {@link #checkpoint()}. This view will be read-only and is NOT updated by
* {@link #checkpoint()}. You must actually {@link #checkpoint()} before an
* {@link ITx#READ_COMMITTED} view will be available. When there is not a
* checkpoint available, the {@link ITx#UNISOLATED} index view will be
* returned.
*/// Note: core impl for all index access methods.
@Override
public ICheckpointProtocol getIndexLocal(final String name,
final long commitTime) {
assertOpen();
if (commitTime == ITx.READ_COMMITTED) {
final long checkpointAddr;
synchronized(name2Addr) {
final Entry entry = name2Addr.getEntry(name);
if (entry == null) {
log.warn("No such index: name=" + name + ", timestamp="
+ TimestampUtility.toString(commitTime));
return null;
}
checkpointAddr = entry.checkpointAddr;
}
/*
* Load from the store.
*
* TODO There is no canonicalizaing mapping for read-only indices
* for the TemporaryStore. Each such request will produce a distinct
* view of that index. This could be very wasteful of resources
* (RAM).
*/
final ICheckpointProtocol ndx = Checkpoint.loadFromCheckpoint(this,
checkpointAddr, true/* readOnly */);
// final BTree btree = BTree
// .load(this, checkpointAddr, true/*readOnly*/);
// btree.setReadOnly(true);
return ndx;
}
if (commitTime == ITx.UNISOLATED) {
return getUnisolatedIndex(name);
}
/**
* FIXME The RWStore uses a read-only transaction to protect against
* recycling of the B+Tree revisions associated with the commit point on
* which it is reading. The temporary store only supports unisolated
* reads, so this is just ignoring the tx specified by the mutation rule
* for reading on the temporary store and going with the unisolated
* index anyway.
*
* As a work around, I have modified the TemporaryStore in the
* JOURNAL_HA_BRANCH to use the unisolated view of the index. This
* allows the RWStore to be used in combination with incremental truth
* maintenance.
*
* In the BOP model, the timestamp is associated with the predicate.
* This means that we can correctly annotate the predicate reading on
* the RWStore with the read-only tx identifier and the predicate
* reading on the TemporaryStore with the UNISOLATED timestamp (0L).
* Therefore, once the RWStore is brought into the QUADS_QUERY_BRANCH we
* can modify the rules to specify UNISOLATED for the reads on the
* TemporaryStore and the tx for the reads on the RWStore and then roll
* back the change to TemporaryStore? to force the use of the unisolated
* index when a read-only tx was specified.
*
* @see
* RWStore / TemporaryStore interaction when performing incremental
* truth maintenance
*/
// throw new UnsupportedOperationException(
// "Not supported: timestamp="
// + TimestampUtility.toString(timestamp));
return getUnisolatedIndex(name);
}
@Override
public SparseRowStore getGlobalRowStore() {
assertOpen();
return globalRowStoreHelper.getGlobalRowStore();
}
final private GlobalRowStoreHelper globalRowStoreHelper = new GlobalRowStoreHelper(this);
@Override
public SparseRowStore getGlobalRowStore(final long timestamp) {
// TODO Perhaps only allowed for UNISOLATED on a TemporaryStore?
return globalRowStoreHelper.get(timestamp);
}
@Override
public BigdataFileSystem getGlobalFileSystem() {
assertOpen();
return globalFileSystemHelper.getGlobalFileSystem();
}
final private GlobalFileSystemHelper globalFileSystemHelper = new GlobalFileSystemHelper(this);
@Override
public DefaultResourceLocator> getResourceLocator() {
assertOpen();
return resourceLocator;
}
private final DefaultResourceLocator> resourceLocator;
@Override
public ExecutorService getExecutorService() {
assertOpen();
return executorService;
}
private final ExecutorService executorService;
@Override
final public IResourceLockService getResourceLockService() {
return resourceLockManager;
}
final private ResourceLockService resourceLockManager = new ResourceLockService();
@Override
public void close() {
// immediate shutdown.
executorService.shutdownNow();
super.close();
}
/**
* {@inheritDoc}
*
* This implementation always returns ZERO (0L) since you can not perform a
* commit on a {@link TemporaryRawStore} (it supports checkpoints but not
* commits).
*/
@Override
public long getLastCommitTime() {
return 0L;
}
/**
* The {@link TemporaryStore} does not support group commit.
*/
@Override
public boolean isGroupCommit() {
return false;
}
/**
* Always returns this {@link TemporaryStore}.
*/
@Override
public TemporaryStore getTempStore() {
return this;
}
/**
* Not supported, returns null
.
*/
@Override
public ScheduledFuture> addScheduledTask(Runnable task,
long initialDelay, long delay, TimeUnit unit) {
return null;
}
/**
* Not supported, returns false
.
*/
@Override
public boolean getCollectPlatformStatistics() {
return false;
}
/**
* Not supported, returns false
.
*/
@Override
public boolean getCollectQueueStatistics() {
return false;
}
/**
* Not supported, returns false
.
*/
@Override
public int getHttpdPort() {
return -1;
}
}