package com.bigdata.bop.engine;
import java.rmi.RemoteException;
import java.util.UUID;
import com.bigdata.bop.BOp;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IQueryContext;
import com.bigdata.bop.fed.FederatedRunningQuery;
import com.bigdata.bop.solutions.SolutionSetStream;
import com.bigdata.btree.Checkpoint;
import com.bigdata.relation.accesspath.EmptyCloseableIterator;
import com.bigdata.relation.accesspath.ThickCloseableIterator;
import com.bigdata.rwstore.sector.MemStore;
import com.bigdata.stream.Stream.StreamIndexMetadata;
import cutthecrap.utils.striterators.ICloseableIterator;
/**
* A chunk of intermediate results stored on the native heap which are ready to
* be consumed by some {@link BOp} in a specific query.
* <p>
* Note: This class is only used in query evaluation for the standalone
* database.
*
* @see BLZG-533 Vector query engine on native heap.
*/
public class LocalNativeChunkMessage implements IChunkMessage<IBindingSet> {
/**
*
*/
private static final long serialVersionUID = 1L;
/** The query controller. */
private final IQueryClient queryController;
/** The service {@link UUID} for the {@link IQueryClient query controller}. */
private final UUID queryControllerId;
/**
* The query identifier.
*/
private final UUID queryId;
/**
* The target {@link BOp}.
*/
private final int bopId;
/**
* The index partition which is being targeted for that {@link BOp}.
*/
private final int partitionId;
/**
* The solutions (or null if the chunk was empty).
*/
private final SolutionSetStream ssstr;
/**
* The memory manager on which the solutions were stored (this is an
* allocation context and can be destroyed without destroying the parent
* memory manager).
*/
private final MemStore mmgr;
/**
* The #of bytes of data written onto the {@link SolutionSetStream} for
* the {@link IChunkMessage}.
*/
private final long byteCount;
/**
* false unless the chunk has been released.
*/
private boolean released;
@Override
public IQueryClient getQueryController() {
return queryController;
}
@Override
public UUID getQueryControllerId() {
return queryControllerId;
}
@Override
public UUID getQueryId() {
return queryId;
}
@Override
public int getBOpId() {
return bopId;
}
@Override
public int getPartitionId() {
return partitionId;
}
@Override
public boolean isLastInvocation() {
return false; // Never.
}
@Override
public boolean isMaterialized() {
return true;
}
@Override
public int getSolutionCount() {
final SolutionSetStream ssstr = this.ssstr;
if (ssstr == null)
return 0;
final long nsolutions = ssstr.getStats().getSolutionSetSize();
if (nsolutions > Integer.MAX_VALUE)
throw new UnsupportedOperationException(
"Too many solutions for interface to report correctly: solutionCount=" + nsolutions);
return (int) nsolutions;
}
/**
* Return the byte count of the solutions on the native heap for this chunk
* message.
*/
public long getByteCount() {
return byteCount;
}
public LocalNativeChunkMessage(final IQueryClient queryController,
final UUID queryId, final int bopId, final int partitionId,
final IQueryContext queryContext,
final IBindingSet[] bsets) {
this(queryController, queryId, bopId, partitionId,
queryContext,
new IBindingSet[][] { bsets });
}
public LocalNativeChunkMessage(final IQueryClient queryController,
final UUID queryId, final int bopId, final int partitionId,
final IQueryContext queryContext,
final IBindingSet[][] bindingSetChunks) {
if (queryController == null)
throw new IllegalArgumentException();
if (queryId == null)
throw new IllegalArgumentException();
if (queryContext == null)
throw new IllegalArgumentException();
if (bindingSetChunks == null)
throw new IllegalArgumentException();
this.queryController = queryController;
try {
this.queryControllerId = queryController.getServiceUUID();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
this.queryId = queryId;
this.bopId = bopId;
this.partitionId = partitionId;
this.released = false;
if (bindingSetChunks.length == 0 || (bindingSetChunks.length == 1 && bindingSetChunks[0].length == 0)) {
/*
* Empty chunk.
*/
this.mmgr = null;
this.ssstr = null;
this.byteCount = 0L;
} else {
/*
* Write the chunk onto a SolutionSetStream.
*/
final StreamIndexMetadata metadata = new StreamIndexMetadata(UUID.randomUUID());
final Checkpoint checkpoint = new Checkpoint(metadata);
final MemStore store = new MemStore(queryContext.getMemoryManager().createAllocationContext());
final SolutionSetStream ssstr = new SolutionSetStream(store, checkpoint, metadata, false /* readOnly */);
// play the result into a native memory backed solution set stream
final ThickCloseableIterator<IBindingSet[]> itr = new ThickCloseableIterator<IBindingSet[]>(
bindingSetChunks);
try {
ssstr.put(itr);
} finally {
itr.close();
}
this.ssstr = ssstr;
this.mmgr = store;
// // Note: This reports the size in bytes on sector boundaries (1M blocks).
// this.byteCount = store.size();
// This reports the actual size of the allocation for the PSOutputStream.
this.byteCount = store.getByteCount(ssstr.getRootAddr());
}
}
@Override
public String toString() {
return getClass().getName() + "{queryId=" + queryId + ",bopId=" + bopId + ",partitionId=" + partitionId
+ ", ssstr=" + (ssstr == null ? "null" : ssstr.toString() + ":" + ssstr.getStats().toString()) + "}";
}
@Override
public void materialize(FederatedRunningQuery runningQuery) {
// NOP
}
@Override
public void release() {
if (!released) {
if (mmgr != null) {
mmgr.destroy();
}
final ChunkAccessor tmp = chunkAccessor;
if (tmp != null) {
// Close the iterator.
tmp.close();
}
released = true;
}
}
@Override
public IChunkAccessor<IBindingSet> getChunkAccessor() {
if (chunkAccessor == null) {
chunkAccessor = new ChunkAccessor();
}
return chunkAccessor;
}
private volatile transient ChunkAccessor chunkAccessor = null;
private class ChunkAccessor implements IChunkAccessor<IBindingSet> {
private final ICloseableIterator<IBindingSet[]> source;
public ChunkAccessor() {
if (released)
throw new IllegalStateException();
if (ssstr == null) {
source = new EmptyCloseableIterator<IBindingSet[]>();
} else {
source = ssstr.get();
}
}
@Override
public ICloseableIterator<IBindingSet[]> iterator() {
return source;
}
public void close() {
source.close();
if (ssstr != null) {
ssstr.close();
}
}
}
}