use of org.apache.drill.common.exceptions.RetryAfterSpillException in project drill by axbaretto.
the class HashAggTemplate method checkGroupAndAggrValues.
// Check if a group is present in the hash table; if not, insert it in the hash table.
// The htIdxHolder contains the index of the group in the hash table container; this same
// index is also used for the aggregation values maintained by the hash aggregate.
private void checkGroupAndAggrValues(int incomingRowIdx) {
assert incomingRowIdx >= 0;
assert !earlyOutput;
/**
* for debugging
* Object tmp = (incoming).getValueAccessorById(0, BigIntVector.class).getValueVector();
* BigIntVector vv0 = null;
* BigIntHolder holder = null;
*
* if (tmp != null) {
* vv0 = ((BigIntVector) tmp);
* holder = new BigIntHolder();
* holder.value = vv0.getAccessor().get(incomingRowIdx) ;
* }
*/
/*
if ( handlingSpills && ( incomingRowIdx == 0 ) ) {
// for debugging -- show the first row from a spilled batch
Object tmp0 = (incoming).getValueAccessorById(NullableVarCharVector.class, 0).getValueVector();
Object tmp1 = (incoming).getValueAccessorById(NullableVarCharVector.class, 1).getValueVector();
Object tmp2 = (incoming).getValueAccessorById(NullableBigIntVector.class, 2).getValueVector();
if (tmp0 != null && tmp1 != null && tmp2 != null) {
NullableVarCharVector vv0 = ((NullableVarCharVector) tmp0);
NullableVarCharVector vv1 = ((NullableVarCharVector) tmp1);
NullableBigIntVector vv2 = ((NullableBigIntVector) tmp2);
logger.debug("The first row = {} , {} , {}", vv0.getAccessor().get(incomingRowIdx), vv1.getAccessor().get(incomingRowIdx), vv2.getAccessor().get(incomingRowIdx));
}
}
*/
// The hash code is computed once, then its lower bits are used to determine the
// partition to use, and the higher bits determine the location in the hash table.
int hashCode;
try {
// htables[0].updateBatches();
hashCode = htables[0].getHashCode(incomingRowIdx);
} catch (SchemaChangeException e) {
throw new UnsupportedOperationException("Unexpected schema change", e);
}
// right shift hash code for secondary (or tertiary...) spilling
for (int i = 0; i < cycleNum; i++) {
hashCode >>>= bitsInMask;
}
int currentPartition = hashCode & partitionMask;
hashCode >>>= bitsInMask;
HashTable.PutStatus putStatus = null;
long allocatedBeforeHTput = allocator.getAllocatedMemory();
// Proactive spill - in case there is no reserve memory - spill and retry putting later
if (reserveValueBatchMemory == 0 && canSpill) {
logger.trace("Reserved memory runs short, trying to {} a partition and retry Hash Table put() again.", is1stPhase ? "early return" : "spill");
// spill to free some memory
doSpill(currentPartition);
retrySameIndex = true;
// to retry this put()
return;
}
// ==========================================
try {
putStatus = htables[currentPartition].put(incomingRowIdx, htIdxHolder, hashCode);
} catch (RetryAfterSpillException re) {
if (!canSpill) {
throw new OutOfMemoryException(getOOMErrorMsg("Can not spill"));
}
logger.trace("HT put failed with an OOM, trying to {} a partition and retry Hash Table put() again.", is1stPhase ? "early return" : "spill");
// for debugging - in case there's a leak
long memDiff = allocator.getAllocatedMemory() - allocatedBeforeHTput;
if (memDiff > 0) {
logger.warn("Leak: HashTable put() OOM left behind {} bytes allocated", memDiff);
}
// spill to free some memory
doSpill(currentPartition);
retrySameIndex = true;
// to retry this put()
return;
} catch (OutOfMemoryException exc) {
throw new OutOfMemoryException(getOOMErrorMsg("HT was: " + allocatedBeforeHTput), exc);
} catch (SchemaChangeException e) {
throw new UnsupportedOperationException("Unexpected schema change", e);
}
long allocatedBeforeAggCol = allocator.getAllocatedMemory();
boolean needToCheckIfSpillIsNeeded = allocatedBeforeAggCol > allocatedBeforeHTput;
//
if (putStatus == HashTable.PutStatus.NEW_BATCH_ADDED) {
try {
// try to preempt an OOM by using the reserve
useReservedValuesMemory();
// allocate a new (internal) values batch
addBatchHolder(currentPartition);
// restore the reserve, if possible
restoreReservedMemory();
// A reason to check for a spill - In case restore-reserve failed
needToCheckIfSpillIsNeeded = (0 == reserveValueBatchMemory);
// just allocated a planned batch
if (plannedBatches > 0) {
plannedBatches--;
}
long totalAddedMem = allocator.getAllocatedMemory() - allocatedBeforeHTput;
long aggValuesAddedMem = allocator.getAllocatedMemory() - allocatedBeforeAggCol;
logger.trace("MEMORY CHECK AGG: allocated now {}, added {}, total (with HT) added {}", allocator.getAllocatedMemory(), aggValuesAddedMem, totalAddedMem);
// resize the batch estimates if needed (e.g., varchars may take more memory than estimated)
if (totalAddedMem > estMaxBatchSize) {
logger.trace("Adjusting Batch size estimate from {} to {}", estMaxBatchSize, totalAddedMem);
estMaxBatchSize = totalAddedMem;
needToCheckIfSpillIsNeeded = true;
}
if (aggValuesAddedMem > estValuesBatchSize) {
logger.trace("Adjusting Values Batch size from {} to {}", estValuesBatchSize, aggValuesAddedMem);
estValuesBatchSize = aggValuesAddedMem;
needToCheckIfSpillIsNeeded = true;
}
} catch (OutOfMemoryException exc) {
throw new OutOfMemoryException(getOOMErrorMsg("AGGR"), exc);
}
} else if (putStatus == HashTable.PutStatus.KEY_ADDED_LAST) {
// If a batch just became full (i.e. another batch would be allocated soon) -- then need to
// check (later, see below) if the memory limits are too close, and if so -- then spill !
// planning to allocate one more batch
plannedBatches++;
needToCheckIfSpillIsNeeded = true;
}
// =================================================================
// Locate the matching aggregate columns and perform the aggregation
// =================================================================
int currentIdx = htIdxHolder.value;
BatchHolder bh = batchHolders[currentPartition].get((currentIdx >>> 16) & HashTable.BATCH_MASK);
int idxWithinBatch = currentIdx & HashTable.BATCH_MASK;
if (bh.updateAggrValues(incomingRowIdx, idxWithinBatch)) {
numGroupedRecords++;
}
// ===================================================================================
if (needToCheckIfSpillIsNeeded && canSpill && useMemoryPrediction) {
spillIfNeeded(currentPartition);
}
}
use of org.apache.drill.common.exceptions.RetryAfterSpillException in project drill by apache.
the class HashAggTemplate method checkGroupAndAggrValues.
// Check if a group is present in the hash table; if not, insert it in the hash table.
// The htIdxHolder contains the index of the group in the hash table container; this same
// index is also used for the aggregation values maintained by the hash aggregate.
private void checkGroupAndAggrValues(int incomingRowIdx) {
assert incomingRowIdx >= 0;
assert !earlyOutput;
// The hash code is computed once, then its lower bits are used to determine the
// partition to use, and the higher bits determine the location in the hash table.
int hashCode;
try {
// htables[0].updateBatches();
hashCode = htables[0].getBuildHashCode(incomingRowIdx);
} catch (SchemaChangeException e) {
throw new UnsupportedOperationException("Unexpected schema change", e);
}
// right shift hash code for secondary (or tertiary...) spilling
for (int i = 0; i < spilledState.getCycle(); i++) {
hashCode >>>= spilledState.getBitsInMask();
}
int currentPartition = hashCode & spilledState.getPartitionMask();
hashCode >>>= spilledState.getBitsInMask();
HashTable.PutStatus putStatus = null;
long allocatedBeforeHTput = allocator.getAllocatedMemory();
String tryingTo = phase.is1st() ? "early return" : "spill";
// Proactive spill - in case there is no reserve memory - spill and retry putting later
if (reserveValueBatchMemory == 0 && canSpill) {
logger.trace("Reserved memory runs short, trying to {} a partition and retry Hash Table put() again.", tryingTo);
// spill to free some memory
doSpill(currentPartition);
retrySameIndex = true;
// to retry this put()
return;
}
// ==========================================
try {
putStatus = htables[currentPartition].put(incomingRowIdx, htIdxHolder, hashCode, getTargetBatchCount());
} catch (RetryAfterSpillException re) {
if (!canSpill) {
throw new OutOfMemoryException(getOOMErrorMsg("Can not spill"));
}
logger.trace("HT put failed with an OOM, trying to {} a partition and retry Hash Table put() again.", tryingTo);
// for debugging - in case there's a leak
long memDiff = allocator.getAllocatedMemory() - allocatedBeforeHTput;
if (memDiff > 0) {
logger.warn("Leak: HashTable put() OOM left behind {} bytes allocated", memDiff);
}
// spill to free some memory
doSpill(currentPartition);
retrySameIndex = true;
// to retry this put()
return;
} catch (OutOfMemoryException exc) {
throw new OutOfMemoryException(getOOMErrorMsg("HT was: " + allocatedBeforeHTput), exc);
} catch (SchemaChangeException e) {
throw new UnsupportedOperationException("Unexpected schema change", e);
}
long allocatedBeforeAggCol = allocator.getAllocatedMemory();
boolean needToCheckIfSpillIsNeeded = allocatedBeforeAggCol > allocatedBeforeHTput;
//
if (putStatus == HashTable.PutStatus.NEW_BATCH_ADDED) {
try {
// try to preempt an OOM by using the reserve
useReservedValuesMemory();
// allocate a new (internal) values batch
addBatchHolder(currentPartition, getTargetBatchCount());
// restore the reserve, if possible
restoreReservedMemory();
// A reason to check for a spill - In case restore-reserve failed
needToCheckIfSpillIsNeeded = (0 == reserveValueBatchMemory);
// just allocated a planned batch
if (plannedBatches > 0) {
plannedBatches--;
}
long totalAddedMem = allocator.getAllocatedMemory() - allocatedBeforeHTput;
long aggValuesAddedMem = allocator.getAllocatedMemory() - allocatedBeforeAggCol;
logger.trace("MEMORY CHECK AGG: allocated now {}, added {}, total (with HT) added {}", allocator.getAllocatedMemory(), aggValuesAddedMem, totalAddedMem);
// resize the batch estimates if needed (e.g., varchars may take more memory than estimated)
if (totalAddedMem > estMaxBatchSize) {
logger.trace("Adjusting Batch size estimate from {} to {}", estMaxBatchSize, totalAddedMem);
estMaxBatchSize = totalAddedMem;
needToCheckIfSpillIsNeeded = true;
}
if (aggValuesAddedMem > estValuesBatchSize) {
logger.trace("Adjusting Values Batch size from {} to {}", estValuesBatchSize, aggValuesAddedMem);
estValuesBatchSize = aggValuesAddedMem;
needToCheckIfSpillIsNeeded = true;
}
} catch (OutOfMemoryException exc) {
throw new OutOfMemoryException(getOOMErrorMsg("AGGR"), exc);
}
} else if (putStatus == HashTable.PutStatus.KEY_ADDED_LAST) {
// If a batch just became full (i.e. another batch would be allocated soon) -- then need to
// check (later, see below) if the memory limits are too close, and if so -- then spill !
// planning to allocate one more batch
plannedBatches++;
needToCheckIfSpillIsNeeded = true;
}
// =================================================================
// Locate the matching aggregate columns and perform the aggregation
// =================================================================
int currentIdx = htIdxHolder.value;
BatchHolder bh = batchHolders[currentPartition].get((currentIdx >>> 16) & BATCH_MASK);
int idxWithinBatch = currentIdx & BATCH_MASK;
if (bh.updateAggrValues(incomingRowIdx, idxWithinBatch)) {
numGroupedRecords++;
}
// ===================================================================================
if (needToCheckIfSpillIsNeeded && canSpill && useMemoryPrediction) {
spillIfNeeded(currentPartition);
}
}
use of org.apache.drill.common.exceptions.RetryAfterSpillException in project drill by axbaretto.
the class HashJoinBatch method executeBuildPhase.
public void executeBuildPhase() throws SchemaChangeException, ClassTransformationException, IOException {
// skip first batch if count is zero, as it may be an empty schema batch
if (isFurtherProcessingRequired(rightUpstream) && right.getRecordCount() == 0) {
for (final VectorWrapper<?> w : right) {
w.clear();
}
rightUpstream = next(right);
if (isFurtherProcessingRequired(rightUpstream) && right.getRecordCount() > 0 && hashTable == null) {
setupHashTable();
}
}
boolean moreData = true;
while (moreData) {
switch(rightUpstream) {
case OUT_OF_MEMORY:
case NONE:
case NOT_YET:
case STOP:
moreData = false;
continue;
case OK_NEW_SCHEMA:
if (rightSchema == null) {
rightSchema = right.getSchema();
if (rightSchema.getSelectionVectorMode() != BatchSchema.SelectionVectorMode.NONE) {
final String errorMsg = new StringBuilder().append("Hash join does not support build batch with selection vectors. ").append("Build batch has selection mode = ").append(left.getSchema().getSelectionVectorMode()).toString();
throw new SchemaChangeException(errorMsg);
}
setupHashTable();
} else {
if (!rightSchema.equals(right.getSchema())) {
throw SchemaChangeException.schemaChanged("Hash join does not support schema changes in build side.", rightSchema, right.getSchema());
}
hashTable.updateBatches();
}
// Fall through
case OK:
final int currentRecordCount = right.getRecordCount();
/* For every new build batch, we store some state in the helper context
* Add new state to the helper context
*/
hjHelper.addNewBatch(currentRecordCount);
// Holder contains the global index where the key is hashed into using the hash table
final IndexPointer htIndex = new IndexPointer();
// For every record in the build batch , hash the key columns
for (int i = 0; i < currentRecordCount; i++) {
int hashCode = hashTable.getHashCode(i);
try {
hashTable.put(i, htIndex, hashCode);
}// Hash Join can not retry yet
catch (RetryAfterSpillException RE) {
throw new OutOfMemoryException("HT put");
}
/* Use the global index returned by the hash table, to store
* the current record index and batch index. This will be used
* later when we probe and find a match.
*/
hjHelper.setCurrentIndex(htIndex.value, buildBatchIndex, i);
}
/* Completed hashing all records in this batch. Transfer the batch
* to the hyper vector container. Will be used when we want to retrieve
* records that have matching keys on the probe side.
*/
final RecordBatchData nextBatch = new RecordBatchData(right, oContext.getAllocator());
boolean success = false;
try {
if (hyperContainer == null) {
hyperContainer = new ExpandableHyperContainer(nextBatch.getContainer());
} else {
hyperContainer.addBatch(nextBatch.getContainer());
}
// completed processing a batch, increment batch index
buildBatchIndex++;
success = true;
} finally {
if (!success) {
nextBatch.clear();
}
}
break;
}
// Get the next record batch
rightUpstream = next(HashJoinHelper.RIGHT_INPUT, right);
}
}
use of org.apache.drill.common.exceptions.RetryAfterSpillException in project drill by apache.
the class HashPartition method buildContainersHashTableAndHelper.
/**
* Creates the hash table and join helper for this partition.
* This method should only be called after all the build side records
* have been consumed.
*/
public void buildContainersHashTableAndHelper() throws SchemaChangeException {
// no building for spilled partitions
if (isSpilled) {
return;
}
containers = new ArrayList<>();
hashTable.updateInitialCapacity((int) getNumInMemoryRecords());
for (int curr = 0; curr < partitionBatchesCount; curr++) {
VectorContainer nextBatch = tmpBatchesList.get(curr);
final int currentRecordCount = nextBatch.getRecordCount();
// For every incoming build batch, we create a matching helper batch
if (!semiJoin) {
hjHelper.addNewBatch(currentRecordCount);
}
// Holder contains the global index where the key is hashed into using the hash table
final IndexPointer htIndex = new IndexPointer();
assert nextBatch != null;
assert probeBatch != null;
hashTable.updateIncoming(nextBatch, probeBatch);
IntVector HV_vector = (IntVector) nextBatch.getLast();
for (int recInd = 0; recInd < currentRecordCount; recInd++) {
int hashCode = HV_vector.getAccessor().get(recInd);
try {
hashTable.put(recInd, htIndex, hashCode, BATCH_SIZE);
} catch (RetryAfterSpillException RE) {
throw new OutOfMemoryException("HT put");
}
/* Use the global index returned by the hash table, to store
* the current record index and batch index. This will be used
* later when we probe and find a match.
*/
if (!semiJoin) {
hjHelper.setCurrentIndex(htIndex.value, curr, /* buildBatchIndex */
recInd);
}
}
containers.add(nextBatch);
}
// the inner is whole in memory, no need for an outer batch
outerBatchAllocNotNeeded = true;
}
Aggregations