Package edu.brown.hstore

Source Code of edu.brown.hstore.TestPartitionLockQueue$BlockingTakeThread

package edu.brown.hstore;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import org.junit.Test;
import org.voltdb.TransactionIdManager;
import org.voltdb.VoltProcedure;
import org.voltdb.catalog.Procedure;
import org.voltdb.catalog.Site;

import edu.brown.BaseTestCase;
import edu.brown.benchmark.tm1.procedures.DeleteCallForwarding;
import edu.brown.hstore.PartitionLockQueue.QueueState;
import edu.brown.hstore.conf.HStoreConf;
import edu.brown.hstore.txns.AbstractTransaction;
import edu.brown.hstore.txns.LocalTransaction;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.PartitionSet;
import edu.brown.utils.ProjectType;
import edu.brown.utils.StringUtil;
import edu.brown.utils.ThreadUtil;

public class TestPartitionLockQueue extends BaseTestCase {

    private static final int NUM_TXNS = 10;
    private static final int TXN_DELAY = 500;
    private static final Class<? extends VoltProcedure> TARGET_PROCEDURE = DeleteCallForwarding.class;
    private static final Random random = new Random(0);
   
    HStoreSite hstore_site;
    HStoreConf hstore_conf;
    TransactionIdManager idManager;
    TransactionQueueManager queueManager;
    PartitionLockQueue queue;
    PartitionLockQueue.Debug queueDbg;
    Procedure catalog_proc;
   
    @Override
    protected void setUp() throws Exception {
        super.setUp(ProjectType.TM1);
       
        this.hstore_conf = HStoreConf.singleton();
        this.hstore_conf.site.txn_incoming_delay = TXN_DELAY;
       
        Site catalog_site = CollectionUtil.first(catalogContext.sites);
        assertNotNull(catalog_site);
        this.hstore_site = new MockHStoreSite(catalog_site.getId(), catalogContext, HStoreConf.singleton());
        this.idManager = hstore_site.getTransactionIdManager(0);
        this.queueManager = this.hstore_site.getTransactionQueueManager();
        this.queue = this.queueManager.getLockQueue(0);
        this.queueDbg = this.queue.getDebugContext();
        assertTrue(this.queue.isEmpty());
       
        this.catalog_proc = this.getProcedure(TARGET_PROCEDURE);
    }
   
    // --------------------------------------------------------------------------------------------
    // UTILITY METHODS
    // --------------------------------------------------------------------------------------------

    private class BlockingTakeThread extends Thread {
        final AtomicReference<AbstractTransaction> result = new AtomicReference<AbstractTransaction>();
        final CountDownLatch latch = new CountDownLatch(1);
       
        { this.setDaemon(true); }
       
        public void run() {
            try {
                AbstractTransaction ts = queue.take();
                System.err.println("AWOKEN: " + ts);
                result.set(ts);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } finally {
                latch.countDown();
            }
        }
    }
   
    private Collection<AbstractTransaction> loadQueue(int num_txns) throws InterruptedException {
        Collection<AbstractTransaction> added = new TreeSet<AbstractTransaction>();
        for (long i = 0; i < num_txns; i++) {
            LocalTransaction txn = new LocalTransaction(this.hstore_site);
            Long txnId = this.idManager.getNextUniqueTransactionId();
            txn.testInit(txnId, 0, new PartitionSet(1), this.catalog_proc);
           
            // I think that we need to do this...
            this.queue.noteTransactionRecievedAndReturnLastSafeTxnId(txn.getTransactionId());
           
            boolean ret = this.queue.offer(txn, false);
            assert(ret);
            added.add(txn);
        } // FOR
        return (added);
    }
   
    // --------------------------------------------------------------------------------------------
    // TEST CASES
    // --------------------------------------------------------------------------------------------
   
    /**
     * testBlockUntilReady
     */
    @Test
    public void testBlockUntilReady() throws Exception {
        // We want to check that we can have one thread block on the queue
        // even though there is a txn in the queue, but whose block time hasn't passed
        // yet. We want to check to see that the thread can be awoke on its own
        // without needing to call checkQueueState()
       
        // Load one txn in the queue.
        Collection<AbstractTransaction> added = this.loadQueue(1);
        assertEquals(1, added.size());
        AbstractTransaction expected = CollectionUtil.first(added);
        assertNotNull(expected);
       
        BlockingTakeThread t = new BlockingTakeThread();
        t.start();

        // Sleep for a little bit to avoid a race condition in our tests
        // The thread should not have finished
        ThreadUtil.sleep(TXN_DELAY);
       
        // Ok now we'll just move time forward. The thread should
        // haven been woken up on its own
        boolean result = t.latch.await(TXN_DELAY, TimeUnit.MILLISECONDS);
        assertTrue(result);
        assertEquals(expected, t.result.get());
    }
   
    /**
     * testBlockOnEmpty
     */
    @Test
    public void testBlockOnEmpty() throws Exception {
        // We want to check that we can have one thread block on the queue when it
        // is empty and then be awoken when it's ready to run
        BlockingTakeThread t = new BlockingTakeThread();
        t.start();

        // Sleep for a little bit to avoid a race condition in our tests
        ThreadUtil.sleep(TXN_DELAY);
        assertNull(t.result.get());
        assertEquals(1, t.latch.getCount());
        assertTrue(t.isAlive());
       
        // Load one txn in the queue.
        // The thread will not be awoken because we have not moved time forward
        Collection<AbstractTransaction> added = this.loadQueue(1);
        AbstractTransaction expected = CollectionUtil.first(added);
        assertNull(t.result.get());
        assertEquals(1, added.size());
       
        // Now sleep and then update the time
        // The thread still won't be woken up
        boolean result = t.latch.await(TXN_DELAY*2, TimeUnit.MILLISECONDS);
        assertTrue(result);
        assertEquals(expected, t.result.get());
    }
   
    /**
     * testThrottling
     */
    @Test
    public void testThrottling() throws Exception {
        // Always start off as empty
        QueueState state = this.queueDbg.checkQueueState();
        assertEquals(QueueState.BLOCKED_EMPTY, state);
       
        int max = NUM_TXNS / 2;
        this.queue.setAllowDecrease(false);
        this.queue.setAllowIncrease(false);
        this.queue.setThrottleThreshold(max);
       
        // Add in the first half of the txns. These should
        // all get inserted with out get blocked
        Collection<AbstractTransaction> added = this.loadQueue(max);
        assertEquals(max, added.size());
        assertTrue(this.queue.isThrottled());
       
        // Now when we add in the rest, we should immediately get blocked
        for (int i = 0; i < NUM_TXNS; i++) {
            Collection<AbstractTransaction> afterThrottle = null;
            try {
                afterThrottle = this.loadQueue(1);
            } catch (AssertionError ex) {
                // IGNORE
            }
            assertNull(afterThrottle);
        } // FOR
        assertEquals(max, added.size());
        assertTrue(this.queue.isThrottled());

        // Make sure that we get unthrottled after we release
        // enough txns
        int release = this.queue.getThrottleRelease();
        for (int i = 0, cnt = (max - release); i < cnt; i++) {
            ThreadUtil.sleep(TXN_DELAY);
//            EstTimeUpdater.update(System.currentTimeMillis());
            this.queueDbg.checkQueueState();
            AbstractTransaction ts = this.queue.poll();
            assertNotNull("i="+i, ts);
        } // FOR
        System.err.println(this.queue.debug());
        assertFalse(this.queue.toString(), this.queue.isThrottled());
    }
   
   
    /**
     * testQueueState
     */
    @Test
    public void testQueueState() throws Exception {
        // Always start off as empty
        QueueState state = this.queueDbg.checkQueueState();
        assertEquals(QueueState.BLOCKED_EMPTY, state);
       
        // Insert a bunch of txns that all have the same initiating timestamp
        Collection<AbstractTransaction> added = this.loadQueue(NUM_TXNS);
        assertEquals(added.size(), this.queue.size());
        System.err.println(StringUtil.join("\n", added));
       
        // Because we haven't moved the current time up, we know that none of
        // the txns should be released now
        state = this.queueDbg.checkQueueState();
//        assertEquals(QueueState.BLOCKED_SAFETY, state);
       
        // Sleep for a little bit to make the current time move forward
        ThreadUtil.sleep(TXN_DELAY);
//        EstTimeUpdater.update(System.currentTimeMillis());

        Iterator<AbstractTransaction> it = added.iterator();
        for (int i = 0; i < NUM_TXNS; i++) {
            // No matter how many times that we call checkQueueState, our
            // blocked timestamp should not change since we haven't released
            // a transaction
            Long lastBlockTime = null;
            long nextBlockTime;
            for (int j = 0; j < 10; j++) {
                // if (j != 0) PartitionLockQueue.LOG.info(StringUtil.SINGLE_LINE.trim());
                String debug = String.format("i=%d / j=%d", i, j);
                state = this.queueDbg.checkQueueState();
                nextBlockTime = this.queueDbg.getBlockedTimestamp();
                // System.err.printf("%s => state=%s / lastBlock=%d\n", debug, state, lastBlockTime);
               
                assertEquals(debug, QueueState.UNBLOCKED, state);
                if (lastBlockTime != null) {
                    assertEquals(debug, lastBlockTime.longValue(), nextBlockTime);
                }
                lastBlockTime = nextBlockTime;
            } // FOR
            assertEquals("i="+i, it.next(), this.queue.poll());
            // PartitionLockQueue.LOG.info(StringUtil.DOUBLE_LINE.trim());
        } // FOR
    }
   
    /**
     * testQueueStateAfterRemove
     */
    @Test
    public void testQueueStateAfterRemove() throws Exception {
        // Always start off as empty
        QueueState state = this.queueDbg.checkQueueState();
        assertEquals(QueueState.BLOCKED_EMPTY, state);
       
        // Insert a bunch of txns that all have the same initiating timestamp
        Collection<AbstractTransaction> added = this.loadQueue(NUM_TXNS);
        assertEquals(added.size(), this.queue.size());
        System.err.println(StringUtil.join("\n", added));
       
        // Because we haven't moved the current time up, we know that none of
        // the txns should be released now
        state = this.queueDbg.checkQueueState();
        // assertEquals(QueueState.BLOCKED_SAFETY, state);
       
        // Sleep for a little bit to make the current time move forward
        ThreadUtil.sleep(TXN_DELAY);
//        EstTimeUpdater.update(System.currentTimeMillis());

        PartitionLockQueue.LOG.info(StringUtil.DOUBLE_LINE.trim());
        Iterator<AbstractTransaction> it = added.iterator();
        for (int i = 0; i < NUM_TXNS; i++) {
            String debug = String.format("i=%d", i);
           
            // Ok so what we're going to do here is peek into the
            // queue and make sure that that our expected txn
            // is the next one that's suppose to pop out
            AbstractTransaction to_remove = it.next();
            state = this.queueDbg.checkQueueState();
            assertEquals(debug, QueueState.UNBLOCKED, state);
            assertEquals(debug, to_remove, this.queue.peek());

            // Then we're going to delete it and make sure that the next txn
            // queued up is not the one that we just removed
            boolean result = this.queue.remove(to_remove);
            assertTrue(debug, result);
            if (i + 1 < NUM_TXNS) {
                assertFalse(debug, this.queue.isEmpty());
                assertEquals(debug, QueueState.UNBLOCKED, this.queue.getQueueState());
            }
            else {
                assertTrue(debug, this.queue.isEmpty());
            }
            PartitionLockQueue.LOG.info(StringUtil.DOUBLE_LINE.trim());
        } // FOR
    }
   
    /**
     * testOutOfOrderInsertion
     */
    @Test
    public void testOutOfOrderInsertion() throws Exception {
        // Create a bunch of txns and then insert them in the wrong order
        // We should be able to get them back in the right order
        Collection<AbstractTransaction> added = new TreeSet<AbstractTransaction>();
        for (long i = 0; i < NUM_TXNS; i++) {
            LocalTransaction txn = new LocalTransaction(this.hstore_site);
            Long txnId = this.idManager.getNextUniqueTransactionId();
            txn.testInit(txnId, 0, new PartitionSet(1), this.catalog_proc);
            added.add(txn);
        } // FOR
        List<AbstractTransaction> shuffled = new ArrayList<AbstractTransaction>(added);
        Collections.shuffle(shuffled, random);
       
        System.err.println(StringUtil.columns(
                "Expected Order:\n" + StringUtil.join("\n", added),
                "Insertion Order:\n" + StringUtil.join("\n", shuffled)
        ));
        System.err.flush();
       
        for (AbstractTransaction txn : shuffled) {
            this.queue.noteTransactionRecievedAndReturnLastSafeTxnId(txn.getTransactionId());
            boolean ret = this.queue.offer(txn, false);
            assert(ret);
            // assertNull(this.queue.poll());
        } // FOR
        assertEquals(added.size(), this.queue.size());
        assertEquals(QueueState.BLOCKED_SAFETY, this.queueDbg.checkQueueState());

        // Now we should be able to remove the first of these mofos
        Iterator<AbstractTransaction> it = added.iterator();
        for (int i = 0; i < NUM_TXNS; i++) {
            ThreadUtil.sleep(TXN_DELAY);
//            EstTimeUpdater.update(System.currentTimeMillis());
            AbstractTransaction expected = it.next();
            assertNotNull(expected);
            if (i == 0) this.queueDbg.checkQueueState();
            assertEquals("i="+i, expected, this.queue.poll());
        } // FOR
    }
   
    /**
     * testOutOfOrderRemoval
     */
    @Test
    public void testOutOfOrderRemoval() throws Exception {
        Collection<AbstractTransaction> added = this.loadQueue(NUM_TXNS);
        assertEquals(added.size(), this.queue.size());
       
        // Now grab the last one and pop it out
        AbstractTransaction last = CollectionUtil.last(added);
        assertTrue(this.queue.remove(last));
        assertFalse(this.queue.contains(last));
       
        // Now we should be able to remove the first of these mofos
        Iterator<AbstractTransaction> it = added.iterator();
        for (int i = 0; i < NUM_TXNS-1; i++) {
            ThreadUtil.sleep(TXN_DELAY);
//            EstTimeUpdater.update(System.currentTimeMillis());
            if (i == 0) this.queueDbg.checkQueueState();
            assertEquals(it.next(), this.queue.poll());
        } // FOR
        assertTrue(this.queue.isEmpty());
    }
   
    /**
     * testRemove
     */
    @Test
    public void testRemove() throws Exception {
        Collection<AbstractTransaction> added = this.loadQueue(1);
        assertEquals(added.size(), this.queue.size());
       
        // Remove the first. Make sure that poll() doesn't return it
        ThreadUtil.sleep(TXN_DELAY*4);
        // System.err.println(StringUtil.repeat("-", 100));
        this.loadQueue(1);
       
        ThreadUtil.sleep(TXN_DELAY*2);
//        EstTimeUpdater.update(System.currentTimeMillis());
        // System.err.println(StringUtil.repeat("-", 100));
        this.queueDbg.checkQueueState();
        AbstractTransaction first = CollectionUtil.first(added);
        assertEquals(first, this.queue.peek());
        assertTrue(first.toString(), this.queue.remove(first));
        assertFalse(first.toString(), this.queue.contains(first));
       
        AbstractTransaction poll = this.queue.poll();
        assertNotSame(first, poll);
    }
   
    /**
     * testRemoveIterator
     */
    @Test
    public void testRemoveIterator() throws Exception {
        List<AbstractTransaction> added = new ArrayList<AbstractTransaction>(this.loadQueue(10));
        assertEquals(added.size(), this.queue.size());
        Collections.shuffle(added);
       
        // Remove them one by one and make sure that the iterator
        // never returns an id that we removed
        Set<AbstractTransaction> removed = new HashSet<AbstractTransaction>();
        for (int i = 0, cnt = added.size(); i < cnt; i++) {
            AbstractTransaction next = added.get(i);
            assertFalse(next.toString(), removed.contains(next));
            assertTrue(next.toString(), this.queue.contains(next));
            assertTrue(next.toString(),this.queue.remove(next));
            removed.add(next);
           
            int it_ctr = 0;
            for (AbstractTransaction txn : this.queue) {
                assertNotNull(txn);
                assertFalse(txn.toString(), removed.contains(txn));
                assertTrue(txn.toString(), added.contains(txn));
                it_ctr++;
            } // FOR
            assertEquals(added.size() - removed.size(), it_ctr);
        } // FOR
    }
   
    /**
     * testConcurrentOfferIterator
     */
    @Test
    public void testConcurrentOfferIterator() throws Exception {
        Collection<AbstractTransaction> added = this.loadQueue(10);
        assertEquals(added.size(), this.queue.size());
       
        LocalTransaction toOffer = new LocalTransaction(this.hstore_site);
        Long txnId = this.idManager.getNextUniqueTransactionId();
        toOffer.testInit(txnId, 0, new PartitionSet(1), this.catalog_proc);
        assertFalse(this.queue.contains(toOffer));
       
        Set<AbstractTransaction> found = new HashSet<AbstractTransaction>();
        for (AbstractTransaction txn : this.queue) {
            if (found.isEmpty()) this.queue.offer(toOffer, false);
            found.add(txn);
        } // FOR
        assertFalse(found.contains(toOffer));
        assertEquals(added.size(), found.size());
    }
   
    /**
     * testPoll
     */
    @Test
    public void testPoll() throws Exception {
        Collection<AbstractTransaction> added = this.loadQueue(NUM_TXNS);
        assertEquals(added.size(), this.queue.size());
       
        Iterator<AbstractTransaction> it = added.iterator();
        for (int i = 0; i < NUM_TXNS; i++) {
            ThreadUtil.sleep(TXN_DELAY);
//            EstTimeUpdater.update(System.currentTimeMillis());
            if (i == 0) this.queueDbg.checkQueueState();
            assertEquals(it.next(), this.queue.poll());
        } // FOR
    }
   
    /**
     * testPollTooEarly
     */
    @Test
    public void testPollTooEarly() throws Exception {
        // Try polling *before* the appropriate wait time
        this.queueDbg.setMaxWaitTime(TXN_DELAY * 5);
        Collection<AbstractTransaction> added = this.loadQueue(1);
        ThreadUtil.sleep(TXN_DELAY * 5);
        added.addAll(this.loadQueue(1));
        assertEquals(added.size(), this.queue.size());
       
        Iterator<AbstractTransaction> it = added.iterator();
        for (int i = 0; i < NUM_TXNS; i++) {
            // Our first poll should return back a txn
            if (i == 0) {
                this.queueDbg.checkQueueState();
                assertEquals(it.next(), this.queue.poll());
            }
            // The second time should be null
            else {
                AbstractTransaction ts = this.queue.poll();
                assertNull("Unexpected txn returned: " + ts, ts);
                break;
            }
        } // FOR
    }
}
TOP

Related Classes of edu.brown.hstore.TestPartitionLockQueue$BlockingTakeThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.