コード例 #1
0
/**
 * Iterate through the table blocks until all the active tuples have been found. Skip dirty tuples
 * and mark them as clean so that they can be copied during the next snapshot.
 */
bool CopyOnWriteIterator::next(TableTuple &out) {
    assert(m_currentBlock != NULL);
    while (true) {
        if (m_blockOffset >= m_currentBlock->unusedTupleBoundry()) {
            if (m_blockIterator == m_end) {
                m_table->snapshotFinishedScanningBlock(m_currentBlock, TBPtr());
                break;
            }
            m_table->snapshotFinishedScanningBlock(m_currentBlock, m_blockIterator.data());
            m_location = m_blockIterator.key();
            m_currentBlock = m_blockIterator.data();
            assert(m_currentBlock->address() == m_location);
            m_blockIterator.data() = TBPtr();
            m_blockOffset = 0;
            m_blockIterator++;
        }
        assert(m_location < m_currentBlock.get()->address() + m_table->m_tableAllocationSize);
        assert(m_location < m_currentBlock.get()->address() + (m_table->m_tupleLength * m_table->m_tuplesPerBlock));
        assert (out.sizeInValues() == m_table->columnCount());
        m_blockOffset++;
        out.move(m_location);
        const bool active = out.isActive();
        const bool dirty = out.isDirty();
        // Return this tuple only when this tuple is not marked as deleted and isn't dirty
        if (active && !dirty) {
            out.setDirtyFalse();
            m_location += m_tupleLength;
            return true;
        } else {
            out.setDirtyFalse();
            m_location += m_tupleLength;
        }
    }
    return false;
}
コード例 #2
0
void AntiCacheEvictionManager::printLRUChain(PersistentTable* table, int max, bool forward)
{
    VOLT_INFO("num tuples in chain: %d", table->getNumTuplesInEvictionChain());
    VOLT_INFO("oldest tuple id: %u", table->getOldestTupleID());
    VOLT_INFO("newest tuple id: %u", table->getNewestTupleID());
    
    char chain[max * 4];
    int tuple_id;
    TableTuple tuple = table->tempTuple();
    
    if(forward)
        tuple_id = table->getOldestTupleID();
    else
        tuple_id = table->getNewestTupleID();
    
    chain[0] = '\0';
    
    int iterations = 0;
    while(iterations < table->getNumTuplesInEvictionChain() && iterations < max)
    {
        strcat(chain, itoa(tuple_id));
        strcat(chain, " ");
        
        tuple.move(table->dataPtrForTuple(tuple_id));
        
        if(forward)
            tuple_id = tuple.getNextTupleInChain();
        else
            tuple_id = tuple.getPreviousTupleInChain();
        
        iterations++;
    }
    
    VOLT_INFO("LRU CHAIN: %s", chain);
}
bool EvictionIterator::next(TableTuple &tuple)
{    
    PersistentTable* ptable = static_cast<PersistentTable*>(table);

    if(current_tuple_id == ptable->getNewestTupleID()) // we've already returned the last tuple in the chain
    {
        VOLT_DEBUG("No more tuples in the chain.");
        return false; 
    }

    if(current_tuple_id == -1) // this is the first call to next
    {
        VOLT_DEBUG("This is the first tuple in the chain.");

        if(ptable->getNumTuplesInEvictionChain() == 0)  // there are no tuples in the chain
        {
            VOLT_DEBUG("There are no tuples in the eviction chain.");
            return false; 
        }
        
        current_tuple_id = ptable->getOldestTupleID(); 
    }
    else  // advance the iterator to the next tuple in the chain
    {        
        current_tuple_id = current_tuple->getTupleID(); 
    }

    current_tuple->move(ptable->dataPtrForTuple(current_tuple_id)); 
    tuple.move(current_tuple->address()); 
    
    VOLT_DEBUG("current_tuple_id = %d", current_tuple_id);
    
    return true; 
}
コード例 #4
0
TableTuple *newTuple(TupleSchema *schema, int idx, long value) {
    TableTuple *tuple = new TableTuple(schema);
    char *data = new char[tuple->tupleLength()];
    memset(data, 0, tuple->tupleLength());
    tuple->move(data);

    tuple->setNValue(idx, ValueFactory::getBigIntValue(value));
    return tuple;
}
コード例 #5
0
ファイル: EvictionIterator.cpp プロジェクト: aayush90/h-store
bool EvictionIterator::next(TableTuple &tuple)
{    
#ifndef ANTICACHE_TIMESTAMPS
    PersistentTable* ptable = static_cast<PersistentTable*>(table);

    if(current_tuple_id == ptable->getNewestTupleID()) // we've already returned the last tuple in the chain
    {
        VOLT_DEBUG("No more tuples in the chain.");
        return false; 
    }

    if(is_first) // this is the first call to next
    {
        is_first = false; 
        VOLT_DEBUG("This is the first tuple in the chain.");

        if(ptable->getNumTuplesInEvictionChain() == 0)  // there are no tuples in the chain
        {
            VOLT_DEBUG("There are no tuples in the eviction chain.");
            return false; 
        }

        current_tuple_id = ptable->getOldestTupleID(); 
    }
    else  // advance the iterator to the next tuple in the chain
    {        
        current_tuple_id = current_tuple->getNextTupleInChain();
    }

    current_tuple->move(ptable->dataPtrForTuple(current_tuple_id)); 
    tuple.move(current_tuple->address()); 

    VOLT_DEBUG("current_tuple_id = %d", current_tuple_id);
#else
    tuple.move(candidates[current_tuple_id].m_addr);
    current_tuple_id++;
    while (candidates[current_tuple_id].m_addr == candidates[current_tuple_id - 1].m_addr) {
        current_tuple_id++;
        if (current_tuple_id == m_size) break;
    }
#endif

    return true; 
}
コード例 #6
0
/**
 * Iterate through the table blocks until all the active tuples have been found. Skip dirty tuples
 * and mark them as clean so that they can be copied during the next snapshot.
 */
bool CopyOnWriteIterator::next(TableTuple &out) {
    if (m_currentBlock == NULL) {
        return false;
    }
    while (true) {
        if (m_blockOffset >= m_currentBlock->unusedTupleBoundary()) {
            if (m_blockIterator == m_end) {
                m_surgeon->snapshotFinishedScanningBlock(m_currentBlock, TBPtr());
                break;
            }
            m_surgeon->snapshotFinishedScanningBlock(m_currentBlock, m_blockIterator.data());

            char *finishedBlock = m_currentBlock->address();

            m_location = m_blockIterator.key();
            m_currentBlock = m_blockIterator.data();
            assert(m_currentBlock->address() == m_location);
            m_blockOffset = 0;

            // Remove the finished block from the map so that it can be released
            // back to the OS if all tuples in the block is deleted.
            //
            // This invalidates the iterators, so we have to get new iterators
            // using the current block's start address. m_blockIterator has to
            // point to the next block, hence the upper_bound() call.
            m_blocks.erase(finishedBlock);
            m_blockIterator = m_blocks.upper_bound(m_currentBlock->address());
            m_end = m_blocks.end();
        }
        assert(m_location < m_currentBlock.get()->address() + m_table->getTableAllocationSize());
        assert(m_location < m_currentBlock.get()->address() + (m_table->getTupleLength() * m_table->getTuplesPerBlock()));
        assert (out.columnCount() == m_table->columnCount());
        m_blockOffset++;
        out.move(m_location);
        const bool active = out.isActive();
        const bool dirty = out.isDirty();

        if (dirty) m_skippedDirtyRows++;
        if (!active) m_skippedInactiveRows++;

        // Return this tuple only when this tuple is not marked as deleted and isn't dirty
        if (active && !dirty) {
            out.setDirtyFalse();
            m_location += m_tupleLength;
            return true;
        } else {
            out.setDirtyFalse();
            m_location += m_tupleLength;
        }
    }
    return false;
}
コード例 #7
0
TEST_F(TableTupleFilterTest, tableTupleFilterTest)
{
    static const int MARKER = 33;

    TempTable* table = getTempTable();
    TableTupleFilter tableFilter;
    tableFilter.init(table);

    int tuplePerBlock = table->getTuplesPerBlock();
    // make sure table spans more than one block
    ASSERT_TRUE(NUM_OF_TUPLES / tuplePerBlock > 1);

    TableTuple tuple = table->tempTuple();
    TableIterator iterator = table->iterator();

    // iterator over and mark every 5th tuple
    int counter = 0;
    std::multiset<int64_t> control_values;

    while(iterator.next(tuple)) {
        if (++counter % 5 == 0) {
            NValue nvalue = tuple.getNValue(1);
            int64_t value = ValuePeeker::peekBigInt(nvalue);
            control_values.insert(value);
            tableFilter.updateTuple(tuple, MARKER);
        }
    }

    TableTupleFilter_iter<MARKER> endItr = tableFilter.end<MARKER>();
    for (TableTupleFilter_iter<MARKER> itr = tableFilter.begin<MARKER>(); itr != endItr; ++itr) {
            uint64_t tupleAddr = tableFilter.getTupleAddress(*itr);
            tuple.move((char *)tupleAddr);
            ASSERT_TRUE(tuple.isActive());
            NValue nvalue = tuple.getNValue(1);
            int64_t value = ValuePeeker::peekBigInt(nvalue);

            ASSERT_FALSE(control_values.empty());
            auto it = control_values.find(value);
            ASSERT_NE(it, control_values.end());
            control_values.erase(it);
    }
    ASSERT_TRUE(control_values.empty());

}
コード例 #8
0
ファイル: ElasticScanner.cpp プロジェクト: migue/voltdb
/**
 * Get the next tuple or return false if none is available.
 */
bool ElasticScanner::next(TableTuple &out)
{
    bool found = false;
    while (!found && continueScan()) {
        assert(m_currentBlockPtr != NULL);
        // Sanity checks.
        assert(m_tuplePtr < m_currentBlockPtr.get()->address() + m_table.getTableAllocationSize());
        assert(m_tuplePtr < m_currentBlockPtr.get()->address() + (m_tupleSize * m_table.getTuplesPerBlock()));
        assert (out.sizeInValues() == m_table.columnCount());
        // Grab the tuple pointer.
        out.move(m_tuplePtr);
        // Shift to the next tuple in block.
        // continueScan() will check if it's the last one in the block.
        m_tupleIndex++;
        m_tuplePtr += m_tupleSize;
        // The next active/non-dirty tuple is return-worthy.
        found = out.isActive() && !out.isDirty();
    }
    return found;
}
コード例 #9
0
ファイル: TupleTracker.cpp プロジェクト: jennieduggan/h-store
int64_t TupleTrackerManager::getPrimaryKey(std::string tableName, uint32_t tupleId){

	Table* table = voltDBEngine->getTable(tableName);

	TableTuple tuple = TableTuple(table->schema());

	tuple.move(table->dataPtrForTuple(tupleId));

	TableIndex *m_index = table->primaryKeyIndex();

	std::vector<int> column_indices_vector = m_index->getColumnIndices();

	std::vector<int>::iterator it = column_indices_vector.begin();
	NValue colValue;

	int i = 0;

	while (it != column_indices_vector.end()) // this is for non composite key
	{
		colValue = tuple.getNValue(*it);
		it++;
		i++;
	}

	return colValue.castAsBigIntAndGetValue();
	//return i;
	//return colValue.isNull();

	/*

	it = std::find(column_indices_vector.begin(), column_indices_vector.end(), tupleId);

		return (int) std:: distance(column_indices_vector.begin(), it);


	TableTuple outputTuple = ... // tuple for your output table
   TableTuple inputTuple = ... // tuple from your tracking table
   TableTuple origTuple = ... // tuple from the original PersistantTable

    foreach (inputTuple in tracking table) {
    // (1) Get offset from inputTuple and move the origTuple to that location
    origTuple.move(table->dataPtrForTuple(tupleId));

    // (2) Now iterate over the pkey column offsets and copy the values into the inputTuple
    int col_idx = 0;
    for (pkey_offset in m_index->getColumnIndices()) {
        NValue colValue = origTuple.getNValue(pkey_offset);
        outputTuple.setNValue(col_idx, colValue);
        col_idx++;
    }

    // (3) Insert outputTuple into output table
     }



	int colCount = (int)column_indices_vector.size();



	if (colCount < tupleId)
		return -1;


	return column_indices_vector[tupleId];




   //*/
}
コード例 #10
0
ファイル: insertexecutor.cpp プロジェクト: DarkDare/voltdb
bool InsertExecutor::p_execute(const NValueArray &params) {
    assert(m_node == dynamic_cast<InsertPlanNode*>(m_abstractNode));
    assert(m_node);
    assert(m_inputTable == dynamic_cast<TempTable*>(m_node->getInputTable()));
    assert(m_inputTable);

    // Target table can be StreamedTable or PersistentTable and must not be NULL
    // Update target table reference from table delegate
    Table* targetTable = m_node->getTargetTable();
    assert(targetTable);
    assert((targetTable == dynamic_cast<PersistentTable*>(targetTable)) ||
            (targetTable == dynamic_cast<StreamedTable*>(targetTable)));

    PersistentTable* persistentTable = m_isStreamed ?
        NULL : static_cast<PersistentTable*>(targetTable);
    TableTuple upsertTuple = TableTuple(targetTable->schema());

    VOLT_TRACE("INPUT TABLE: %s\n", m_inputTable->debug().c_str());

    // count the number of successful inserts
    int modifiedTuples = 0;

    Table* outputTable = m_node->getOutputTable();
    assert(outputTable);

    TableTuple templateTuple = m_templateTuple.tuple();

    std::vector<int>::iterator it;
    for (it = m_nowFields.begin(); it != m_nowFields.end(); ++it) {
        templateTuple.setNValue(*it, NValue::callConstant<FUNC_CURRENT_TIMESTAMP>());
    }

    VOLT_DEBUG("This is a %s-row insert on partition with id %d",
               m_node->getChildren()[0]->getPlanNodeType() == PLAN_NODE_TYPE_MATERIALIZE ?
               "single" : "multi", m_engine->getPartitionId());
    VOLT_DEBUG("Offset of partition column is %d", m_partitionColumn);

    //
    // An insert is quite simple really. We just loop through our m_inputTable
    // and insert any tuple that we find into our targetTable. It doesn't get any easier than that!
    //
    TableTuple inputTuple(m_inputTable->schema());
    assert (inputTuple.sizeInValues() == m_inputTable->columnCount());
    TableIterator iterator = m_inputTable->iterator();
    while (iterator.next(inputTuple)) {

        for (int i = 0; i < m_node->getFieldMap().size(); ++i) {
            // Most executors will just call setNValue instead of
            // setNValueAllocateForObjectCopies.
            //
            // However, We need to call
            // setNValueAlocateForObjectCopies here.  Sometimes the
            // input table's schema has an inlined string field, and
            // it's being assigned to the target table's outlined
            // string field.  In this case we need to tell the NValue
            // where to allocate the string data.
            templateTuple.setNValueAllocateForObjectCopies(m_node->getFieldMap()[i],
                                                           inputTuple.getNValue(i),
                                                           ExecutorContext::getTempStringPool());
        }

        VOLT_TRACE("Inserting tuple '%s' into target table '%s' with table schema: %s",
                   templateTuple.debug(targetTable->name()).c_str(), targetTable->name().c_str(),
                   targetTable->schema()->debug().c_str());

        // if there is a partition column for the target table
        if (m_partitionColumn != -1) {

            // get the value for the partition column
            NValue value = templateTuple.getNValue(m_partitionColumn);
            bool isLocal = m_engine->isLocalSite(value);

            // if it doesn't map to this site
            if (!isLocal) {
                if (!m_multiPartition) {
                    throw ConstraintFailureException(
                            dynamic_cast<PersistentTable*>(targetTable),
                            templateTuple,
                            "Mispartitioned tuple in single-partition insert statement.");
                }

                // don't insert
                continue;
            }
        }

        // for multi partition export tables, only insert into one
        // place (the partition with hash(0)), if the data is from a
        // replicated source.  If the data is coming from a subquery
        // with partitioned tables, we need to perform the insert on
        // every partition.
        if (m_isStreamed && m_multiPartition && !m_sourceIsPartitioned) {
            bool isLocal = m_engine->isLocalSite(ValueFactory::getBigIntValue(0));
            if (!isLocal) continue;
        }


        if (! m_isUpsert) {
            // try to put the tuple into the target table

            if (m_hasPurgeFragment) {
                if (!executePurgeFragmentIfNeeded(&persistentTable))
                    return false;
                // purge fragment might have truncated the table, and
                // refreshed the persistent table pointer.  Make sure to
                // use it when doing the insert below.
                targetTable = persistentTable;
            }

            if (!targetTable->insertTuple(templateTuple)) {
                VOLT_ERROR("Failed to insert tuple from input table '%s' into"
                           " target table '%s'",
                           m_inputTable->name().c_str(),
                           targetTable->name().c_str());
                return false;
            }

        } else {
            // upsert execution logic
            assert(persistentTable->primaryKeyIndex() != NULL);
            TableTuple existsTuple = persistentTable->lookupTupleByValues(templateTuple);

            if (existsTuple.isNullTuple()) {
                // try to put the tuple into the target table

                if (m_hasPurgeFragment) {
                    if (!executePurgeFragmentIfNeeded(&persistentTable))
                        return false;
                }

                if (!persistentTable->insertTuple(templateTuple)) {
                    VOLT_ERROR("Failed to insert tuple from input table '%s' into"
                               " target table '%s'",
                               m_inputTable->name().c_str(),
                               persistentTable->name().c_str());
                    return false;
                }
            } else {
                // tuple exists already, try to update the tuple instead
                upsertTuple.move(templateTuple.address());
                TableTuple &tempTuple = persistentTable->getTempTupleInlined(upsertTuple);

                if (!persistentTable->updateTupleWithSpecificIndexes(existsTuple, tempTuple,
                        persistentTable->allIndexes())) {
                    VOLT_INFO("Failed to update existsTuple from table '%s'",
                            persistentTable->name().c_str());
                    return false;
                }
            }
        }

        // successfully inserted or updated
        modifiedTuples++;
    }

    TableTuple& count_tuple = outputTable->tempTuple();
    count_tuple.setNValue(0, ValueFactory::getBigIntValue(modifiedTuples));
    // try to put the tuple into the output table
    if (!outputTable->insertTuple(count_tuple)) {
        VOLT_ERROR("Failed to insert tuple count (%d) into"
                   " output table '%s'",
                   modifiedTuples,
                   outputTable->name().c_str());
        return false;
    }

    // add to the planfragments count of modified tuples
    m_engine->addToTuplesModified(modifiedTuples);
    VOLT_DEBUG("Finished inserting %d tuples", modifiedTuples);
    return true;
}
コード例 #11
0
bool AntiCacheEvictionManager::removeTupleSingleLinkedList(PersistentTable* table, uint32_t removal_id) {
    bool tuple_found = false; 
    
    int tuples_in_chain; 
    
    // ids for iterating through the list
    uint32_t current_tuple_id;
    uint32_t previous_tuple_id;
    uint32_t next_tuple_id;
    uint32_t newest_tuple_id;
    
    // assert we have tuples in the eviction chain before we try to remove anything
    tuples_in_chain = table->getNumTuplesInEvictionChain(); 

    if (tuples_in_chain <= 0)
        return false; 

    previous_tuple_id = 0; 
    current_tuple_id = table->getOldestTupleID(); 
    newest_tuple_id = table->getNewestTupleID(); 
    
    // set the tuple to the first tuple in the chain (i.e. oldest)
    TableTuple tuple = table->tempTuple();
    tuple.move(table->dataPtrForTuple(current_tuple_id)); 
    
    // we're removing the head  of the chain, i.e. the oldest tuple
    if (table->getOldestTupleID() == removal_id) {
        //VOLT_INFO("Removing the first tuple in the eviction chain."); 
        if (table->getNumTuplesInEvictionChain() == 1) { // this is the only tuple in the chain
            table->setOldestTupleID(0); 
            table->setNewestTupleID(0); 
        } else {
            next_tuple_id = tuple.getNextTupleInChain();
            table->setOldestTupleID(next_tuple_id); 
        }
        tuple_found = true; 
    }

    int iterations = 0; 
    while(!tuple_found && iterations < table->getNumTuplesInEvictionChain()) {        
        // we've found the tuple we want to remove
        if (current_tuple_id == removal_id) {
            next_tuple_id = tuple.getNextTupleInChain(); 
            
            // create a tuple from the previous tuple id in the chain
            tuple.move(table->dataPtrForTuple(previous_tuple_id)); 
            
            // set the previous tuple to point to the next tuple
            tuple.setNextTupleInChain(next_tuple_id); 
            
            tuple_found = true; 
            break; 
        }
        
        // advance pointers
        previous_tuple_id = current_tuple_id; 
        current_tuple_id = tuple.getNextTupleInChain(); 
        tuple.move(table->dataPtrForTuple(current_tuple_id));
        
        iterations++; 
    }
    
    if (current_tuple_id == newest_tuple_id && !tuple_found) { // we are at the back of the chain
        if (current_tuple_id == removal_id) { // we're removing the back of the chain
            // set the previous tuple pointer to 0 since it is now the back of the chain
            tuple.move(table->dataPtrForTuple(previous_tuple_id)); 
            tuple.setNextTupleInChain(0);
            table->setNewestTupleID(previous_tuple_id); 
            tuple_found = true; 
        }
    }
    
    if (tuple_found) {
        --tuples_in_chain; 
        table->setNumTuplesInEvictionChain(tuples_in_chain); 
        
        return true; 
    }
    
    return false; 
}
コード例 #12
0
// for the double linked list we start from the tail of the chain and iterate backwards
bool AntiCacheEvictionManager::removeTupleDoubleLinkedList(PersistentTable* table, uint32_t removal_id) {
    
    bool tuple_found = false;
    
    int tuples_in_chain;
        
    // ids for iterating through the list
    uint32_t current_tuple_id;
    uint32_t previous_tuple_id;
    uint32_t next_tuple_id;
    uint32_t oldest_tuple_id;
    
    // assert we have tuples in the eviction chain before we try to remove anything
    tuples_in_chain = table->getNumTuplesInEvictionChain();
    
    if (tuples_in_chain <= 0)
        return false;
    
    previous_tuple_id = 0;
    oldest_tuple_id = table->getOldestTupleID();
    current_tuple_id =  table->getNewestTupleID(); // start iteration at back of chain
    
    // set the tuple to the back of the chain (i.e. the newest)
    TableTuple tuple = table->tempTuple();
    tuple.move(table->dataPtrForTuple(current_tuple_id));
        
    // we're removing the tail  of the chain, i.e. the newest tuple
    if (table->getNewestTupleID() == removal_id) {
                
        if (table->getNumTuplesInEvictionChain() == 1) { // this is the only tuple in the chain
            table->setOldestTupleID(0);
            table->setNewestTupleID(0);
        } else if(table->getNumTuplesInEvictionChain() == 2) {
            table->setNewestTupleID(oldest_tuple_id);
            table->setOldestTupleID(oldest_tuple_id);
        }
        else {
            
            tuple.move(table->dataPtrForTuple(table->getNewestTupleID()));
            
            // we need the previous tuple in the chain, since we're iterating from back to front
            previous_tuple_id = tuple.getPreviousTupleInChain();
            table->setNewestTupleID(previous_tuple_id);
        }
        tuple_found = true;
    }
    
    // we're removing the head of the chain, i.e. the oldest tuple
    if(table->getOldestTupleID() == removal_id && !tuple_found) {
                
        if (table->getNumTuplesInEvictionChain() == 1) { // this is the only tuple in the chain
            table->setOldestTupleID(0);
            table->setNewestTupleID(0);
        }
        else if(table->getNumTuplesInEvictionChain() == 2) {
            table->setNewestTupleID(table->getNewestTupleID());
            table->setOldestTupleID(table->getNewestTupleID());
        }
        else {
            
            tuple.move(table->dataPtrForTuple(table->getOldestTupleID()));
            
            next_tuple_id = tuple.getNextTupleInChain();
            table->setOldestTupleID(next_tuple_id);
        }
        tuple_found = true;
    }
    
    int iterations = 0;
    while(!tuple_found && iterations < table->getNumTuplesInEvictionChain()) {
                
        if(current_tuple_id == oldest_tuple_id)
            break;
        
        // we've found the tuple we want to remove
        if (current_tuple_id == removal_id) {
            next_tuple_id = tuple.getPreviousTupleInChain();
            
            // point previous tuple in chain to next tuple
            tuple.move(table->dataPtrForTuple(previous_tuple_id));
            tuple.setPreviousTupleInChain(next_tuple_id);
            
            // point next tuple in chain to previous tuple
            tuple.move(table->dataPtrForTuple(next_tuple_id));
            tuple.setNextTupleInChain(previous_tuple_id);
            
            tuple_found = true;
            break;
        }
        
        // advance pointers
        previous_tuple_id = current_tuple_id;
        current_tuple_id = tuple.getPreviousTupleInChain(); // iterate back to front
        tuple.move(table->dataPtrForTuple(current_tuple_id));
        
        iterations++;
    }
    
    if (tuple_found) {
        tuples_in_chain = table->getNumTuplesInEvictionChain(); 
        --tuples_in_chain;
        table->setNumTuplesInEvictionChain(tuples_in_chain);
        
        return true;
    }
    
    return false; 
}