/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ package org.pentaho.di.core; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import org.pentaho.di.core.row.RowMetaInterface; /** * Contains a buffer of rows. Instead of passing rows along the chain immediately we will batch them up to lower locking * overhead. The row set will start in accepting mode (accepting = true) It will remain there until the buffer is full. * Then it will switch to delivering mode (accepting = false) It will remain there until the buffer is empty. * * When the row set is done and no more rows will be entering the row set we will switch to delivering mode. * * @author Matt * @since 04-05-2011 * */ public class BlockingBatchingRowSet extends BaseRowSet implements Comparable<RowSet>, RowSet { private BlockingQueue<Object[][]> putArray, getArray; private int putIndex, getIndex; private Object[][] inputBuffer, outputBuffer; private int size; private final int BATCHSIZE = 2; // private long inputTID = -1, outputTID = -1; /** * Create new non-blocking-queue with maxSize capacity. * * @param maxSize */ public BlockingBatchingRowSet( int maxSize ) { super(); // create a fixed sized queue for max performance // putArray = new ArrayBlockingQueue<Object[][]>( BATCHSIZE, true ); getArray = new ArrayBlockingQueue<Object[][]>( BATCHSIZE, true ); size = maxSize / BATCHSIZE; // each buffer's size Object[][] buffer; for ( int i = 0; i < BATCHSIZE; i++ ) { buffer = new Object[size][]; putArray.offer( buffer ); } outputBuffer = null; putIndex = getIndex = size; } @Override public boolean putRow( RowMetaInterface rowMeta, Object[] rowData ) { return putRowWait( rowMeta, rowData, Const.TIMEOUT_PUT_MILLIS, TimeUnit.MILLISECONDS ); } /** * We need to wait until */ @Override public boolean putRowWait( RowMetaInterface rowMeta, Object[] rowData, long time, TimeUnit tu ) { this.rowMeta = rowMeta; // If we're not accepting we block until we do // if ( inputBuffer == null ) { try { inputBuffer = putArray.poll( time, tu ); } catch ( InterruptedException e ) { return false; } if ( inputBuffer == null ) { return false; } putIndex = 0; } inputBuffer[putIndex++] = rowData; if ( putIndex == size ) { try { getArray.offer( inputBuffer, time, tu ); inputBuffer = null; } catch ( InterruptedException e ) { return false; } } return true; } // default getRow with wait time = 100ms // /* * (non-Javadoc) * * @see org.pentaho.di.core.RowSetInterface#getRow() */ @Override public Object[] getRow() { return getRowWait( Const.TIMEOUT_GET_MILLIS, TimeUnit.MILLISECONDS ); } /* * (non-Javadoc) * * @see org.pentaho.di.core.RowSetInterface#getRowImmediate() */ @Override public Object[] getRowImmediate() { return getRow(); } @Override public Object[] getRowWait( long timeout, TimeUnit tu ) { if ( outputBuffer == null ) { try { outputBuffer = getArray.poll( timeout, tu ); } catch ( InterruptedException e ) { return null; } if ( outputBuffer == null ) { return null; } getIndex = 0; } Object[] row = outputBuffer[getIndex]; outputBuffer[getIndex++] = null; // prevent any hold-up to GC if ( getIndex == size ) { putArray.offer( outputBuffer ); outputBuffer = null; } return row; } @Override public int size() { // does BlockingQueue.size() grab a lock? If so, frequent call to this method // may stress the locking system return size - getIndex + size * getArray.size(); } @Override public void setDone() { super.setDone(); if ( putIndex > 0 && putIndex < size && inputBuffer != null ) { inputBuffer[putIndex] = null; // signal the end of buffer for ( int i = putIndex + 1; i < size; i++ ) { inputBuffer[i] = null; } getArray.offer( inputBuffer ); } putArray.clear(); } @Override public void clear() { putArray.clear(); getArray.clear(); done.set( false ); } }