Android Open Source - couchbase-lite-android Batcher Test






From Project

Back to project page couchbase-lite-android.

License

The source code is released under:

Apache License

If you think the Android project couchbase-lite-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.couchbase.lite.support;
//from  ww  w.  j a  v a 2  s .  c  o m
import com.couchbase.lite.Database;
import com.couchbase.lite.LiteTestCase;
import com.couchbase.lite.replicator.Replication;
import com.couchbase.lite.util.Log;
import com.couchbase.lite.util.SystemLogger;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class BatcherTest extends LiteTestCase {

    /**
     * Add 100 items in a batcher and make sure that the processor
     * is correctly called back with the first batch.
     *
     */
    public void testBatcherSingleBatch() throws Exception {

        int numBatches = 3;
        final CountDownLatch doneSignal = new CountDownLatch(numBatches);

        ScheduledExecutorService workExecutor = new ScheduledThreadPoolExecutor(1);

        int inboxCapacity = 10;
        int processorDelay = 200;

        Batcher batcher = new Batcher<String>(workExecutor, inboxCapacity, processorDelay, new BatchProcessor<String>() {

            @Override
            public void process(List<String> itemsToProcess) {
                Log.v(TAG, "process called with: " + itemsToProcess);

                assertEquals(10, itemsToProcess.size());

                assertNumbersConsecutive(itemsToProcess);

                doneSignal.countDown();
            }

        });

        ArrayList<String> objectsToQueue = new ArrayList<String>();
        for (int i=0; i<inboxCapacity * numBatches; i++) {
            objectsToQueue.add(Integer.toString(i));
        }
        batcher.queueObjects(objectsToQueue);

        boolean didNotTimeOut = doneSignal.await(35, TimeUnit.SECONDS);
        assertTrue(didNotTimeOut);

    }

    /**
     * With a batcher that has an inbox of size 10, add 10 * x items in batches
     * of 5.  Make sure that the processor is called back with all 10 * x items.
     * Also make sure that they appear in the correct order within a batch.
     */
    public void testBatcherBatchSize5() throws Exception {


        ScheduledExecutorService workExecutor = new ScheduledThreadPoolExecutor(1);

        int inboxCapacity = 10;
        int numItemsToSubmit = inboxCapacity * 2;
        final int processorDelay = 0;

        final CountDownLatch doneSignal = new CountDownLatch(numItemsToSubmit);

        Batcher batcher = new Batcher<String>(workExecutor, inboxCapacity, processorDelay, new BatchProcessor<String>() {

            @Override
            public void process(List<String> itemsToProcess) {
                Log.v(Database.TAG, "process called with: " + itemsToProcess);

                assertNumbersConsecutive(itemsToProcess);

                for (String item : itemsToProcess) {
                    doneSignal.countDown();
                }

                Log.v(Database.TAG, "doneSignal: " + doneSignal.getCount());

            }

        });

        ArrayList<String> objectsToQueue = new ArrayList<String>();
        for (int i=0; i<numItemsToSubmit; i++) {
            objectsToQueue.add(Integer.toString(i));
            if (objectsToQueue.size() == 5) {
                batcher.queueObjects(objectsToQueue);
                objectsToQueue = new ArrayList<String>();
            }

        }

        boolean didNotTimeOut = doneSignal.await(35, TimeUnit.SECONDS);
        assertTrue(didNotTimeOut);

    }

    /**
     * Reproduce issue:
     * https://github.com/couchbase/couchbase-lite-java-core/issues/283
     *
     * This sporadically fails on the genymotion emulator and Nexus 5 device.
     */
    public void testBatcherThreadSafe() throws Exception {

        // 10 threads using the same batcher

        // each thread queues a bunch of items and makes sure they were all processed

        ScheduledExecutorService workExecutor = new ScheduledThreadPoolExecutor(1);
        int inboxCapacity = 10;
        final int processorDelay = 200;

        int numThreads = 5;
        final int numItemsPerThread = 20;
        int numItemsTotal = numThreads * numItemsPerThread;
        final AtomicInteger numItemsProcessed = new AtomicInteger(0);

        final CountDownLatch allItemsProcessed = new CountDownLatch(numItemsTotal);

        final Batcher batcher = new Batcher<String>(workExecutor, inboxCapacity, processorDelay, new BatchProcessor<String>() {

            @Override
            public void process(List<String> itemsToProcess) {
                for (String item : itemsToProcess) {
                    int curVal = numItemsProcessed.incrementAndGet();
                    Log.d(Log.TAG, "%d items processed so far", curVal);
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    allItemsProcessed.countDown();
                }
            }

        });


        for (int i=0; i<numThreads; i++) {
            final String iStr = Integer.toString(i);
            Runnable runnable = new Runnable() {
                @Override
                public void run() {

                    for (int j=0; j<numItemsPerThread; j++) {
                        try {
                            Thread.sleep(5);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        String item = String.format("%s-item:%d", iStr, j);
                        batcher.queueObject(item);
                    }
                }
            };
            new Thread(runnable).start();


        }

        Log.d(TAG, "waiting for allItemsProcessed");
        boolean success = allItemsProcessed.await(120, TimeUnit.SECONDS);
        assertTrue(success);
        Log.d(TAG, "/waiting for allItemsProcessed");

        assertEquals(numItemsTotal, numItemsProcessed.get());
        assertEquals(0, batcher.count());

        Log.d(TAG, "waiting for pending futures");
        batcher.waitForPendingFutures();
        Log.d(TAG, "/waiting for pending futures");


    }

    /**
     * - Fill batcher up to capacity
     * - Expected behavior: should invoke BatchProcessor almost immediately
     * - Add a single element to batcher
     * - Expected behavior: after processing delay has expired, should invoke BatchProcessor
     *
     * https://github.com/couchbase/couchbase-lite-java-core/issues/329
     */
    public void testBatcherWaitsForProcessorDelay1() throws Exception {

        long timeBeforeQueue;
        long timeAfterCallback;
        long delta;
        boolean success;

        for (int k=0; k<5; k++) {

            ScheduledExecutorService workExecutor = new ScheduledThreadPoolExecutor(1);
            int inboxCapacity = 5;
            int processorDelay = 1000;

            final CountDownLatch latch = new CountDownLatch(1);


            final Batcher batcher = new Batcher<String>(workExecutor, inboxCapacity, processorDelay, new BatchProcessor<String>() {
                @Override
                public void process(List<String> itemsToProcess) {
                    Log.d(TAG, "process() called with %d items", itemsToProcess.size());
                    latch.countDown();
                }
            });

            // add a single object
            timeBeforeQueue = System.currentTimeMillis();
            batcher.queueObject(new String());

            // we shouldn't see latch close until processorDelay milliseconds has passed
            success = latch.await(5, TimeUnit.SECONDS);
            assertTrue(success);
            timeAfterCallback = System.currentTimeMillis();
            delta = timeAfterCallback - timeBeforeQueue;
            assertTrue(delta > 0);
            assertTrue(delta >= processorDelay);

        }


    }


    /**
     * - Fill batcher up to capacity
     * - Expected behavior: should invoke BatchProcessor almost immediately
     * - Add a single element to batcher
     * - Expected behavior: after processing delay has expired, should invoke BatchProcessor
     *
     * https://github.com/couchbase/couchbase-lite-java-core/issues/329
     */
    public void testBatcherWaitsForProcessorDelay2() throws Exception {

        long timeBeforeQueue;
        long timeAfterCallback;
        long delta;
        boolean success;

        for (int k=0; k<5; k++) {

            ScheduledExecutorService workExecutor = new ScheduledThreadPoolExecutor(1);
            int inboxCapacity = 5;
            int processorDelay = 1000;

            final BlockingQueue<CountDownLatch> latches = new LinkedBlockingQueue<CountDownLatch>();
            CountDownLatch latch1 = new CountDownLatch(1);
            CountDownLatch latch2 = new CountDownLatch(1);
            latches.add(latch1);
            latches.add(latch2);

            final Batcher batcher = new Batcher<String>(workExecutor, inboxCapacity, processorDelay, new BatchProcessor<String>() {
                @Override
                public void process(List<String> itemsToProcess) {
                    try {
                        Log.d(TAG, "process() called with %d items", itemsToProcess.size());
                        CountDownLatch latch = latches.take();
                        latch.countDown();
                    } catch (InterruptedException e) {
                        assertFalse(true);
                        throw new RuntimeException(e);
                    }
                }
            });

            // fill up batcher capacity
            for (int i = 0; i < inboxCapacity; i++) {
                batcher.queueObject(new String());
            }

            // latch should have been closed nearly immediately
            success = latch1.await(500, TimeUnit.MILLISECONDS);
            assertTrue(success);

            // add another object
            timeBeforeQueue = System.currentTimeMillis();
            batcher.queueObject(new String());

            // we shouldn't see latch close until processorDelay milliseconds has passed
            success = latch2.await(5, TimeUnit.SECONDS);
            assertTrue(success);
            timeAfterCallback = System.currentTimeMillis();
            delta = timeAfterCallback - timeBeforeQueue;
            assertTrue(delta > 0);
            assertTrue(delta >= processorDelay);
        }


    }

    /**
     * - Add jobs with 10x the capacity
     * - Call waitForPendingFutures
     * - Make sure all jobs are processed before waitForPendingFutures returns
     *
     * https://github.com/couchbase/couchbase-lite-java-core/issues/329
     */
    public void testWaitForPendingFutures() throws Exception {

        ScheduledExecutorService workExecutor = new ScheduledThreadPoolExecutor(1);
        int inboxCapacity = 3;
        int processorDelay = 100;
        int numItemsToSubmit = 30;

        final CountDownLatch latch = new CountDownLatch(numItemsToSubmit);


        final Batcher batcher = new Batcher<String>(workExecutor, inboxCapacity, processorDelay, new BatchProcessor<String>() {
            @Override
            public void process(List<String> itemsToProcess) {
                Log.d(TAG, "process() called with %d items", itemsToProcess.size());
                for (String item : itemsToProcess) {
                    latch.countDown();
                }
            }
        });

        // add numItemsToSubmit to batcher in one swoop
        ArrayList<String> objectsToQueue = new ArrayList<String>();
        for (int i=0; i<numItemsToSubmit; i++) {
            objectsToQueue.add(Integer.toString(i));
        }
        batcher.queueObjects(objectsToQueue);

        // wait until all work drains
        batcher.waitForPendingFutures();

        // at this point, the countdown latch should be 0
        assertEquals(0, latch.getCount());

    }


    /**
     * - Call batcher to queue a single item in a fast loop
     * - As soon as we've hit capacity, it should call processor shortly after
     */
    public void testInvokeProcessorAfterReachingCapacity() throws Exception {

        ScheduledExecutorService workExecutor = new ScheduledThreadPoolExecutor(1);
        final int inboxCapacity = 10;
        final int processorDelay = 500;
        final int numItemsToSubmit = inboxCapacity * 100;
        final int jobDelay = 50;

        final CountDownLatch latchFirstProcess = new CountDownLatch(1);
        final CountDownLatch latchSubmittedCapacity = new CountDownLatch(1);
        final CountDownLatch latchInterrupted = new CountDownLatch(1);

        final Batcher batcher = new Batcher<String>(workExecutor, inboxCapacity, processorDelay, new BatchProcessor<String>() {
            @Override
            public void process(List<String> itemsToProcess) {
                Log.d(TAG, "process() called with %d items", itemsToProcess.size());
                try {
                    Thread.sleep(jobDelay);
                    latchFirstProcess.countDown();
                    Log.d(TAG, "/process() called with %d items", itemsToProcess.size());
                } catch (InterruptedException e) {
                    latchInterrupted.countDown();
                    e.printStackTrace();
                }
            }
        });

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0; i<numItemsToSubmit; i++) {

                    if (i == inboxCapacity) {
                        latchSubmittedCapacity.countDown();
                    }

                    ArrayList<String> objectsToQueue = new ArrayList<String>();
                    objectsToQueue.add(Integer.toString(i));
                    batcher.queueObjects(objectsToQueue);
                    Log.d(TAG, "Submitted object %d", i);

                }
            }
        });
        t.start();

        boolean success = latchSubmittedCapacity.await(5, TimeUnit.SECONDS);
        assertTrue(success);

        // since we've already submitted up to capacity, our processor should
        // be called nearly immediately afterwards
        success = latchFirstProcess.await(jobDelay * 2, TimeUnit.MILLISECONDS);
        assertTrue(success);

        // we should not have been interrupted either
        assertEquals(latchInterrupted.getCount(), 1);

        t.interrupt();

    }



    private static void assertNumbersConsecutive(List<String> itemsToProcess) {
        int previousItemNumber = -1;
        for (String itemString : itemsToProcess) {
            if (previousItemNumber == -1) {
                previousItemNumber = Integer.parseInt(itemString);
            } else {
                int curItemNumber = Integer.parseInt(itemString);
                assertTrue(curItemNumber == previousItemNumber + 1);
                previousItemNumber = curItemNumber;
            }
        }
    }

}




Java Source Code List

com.couchbase.lite.ApiTest.java
com.couchbase.lite.AttachmentsTest.java
com.couchbase.lite.AuthTest.java
com.couchbase.lite.Base64Test.java
com.couchbase.lite.BlobStoreWriterTest.java
com.couchbase.lite.CRUDOperationsTest.java
com.couchbase.lite.CacheTest.java
com.couchbase.lite.ChangesTest.java
com.couchbase.lite.CollationTest.java
com.couchbase.lite.DatabaseTest.java
com.couchbase.lite.DocumentTest.java
com.couchbase.lite.LitePerfTestCase.java
com.couchbase.lite.LiteTestCase.java
com.couchbase.lite.LiteTestContext.java
com.couchbase.lite.LocalDocsTest.java
com.couchbase.lite.ManagerTest.java
com.couchbase.lite.MiscTest.java
com.couchbase.lite.MultipartReaderTest.java
com.couchbase.lite.RevTreeTest.java
com.couchbase.lite.RevisionsTest.java
com.couchbase.lite.RouterTest.java
com.couchbase.lite.SequenceMapTest.java
com.couchbase.lite.ValidationsTest.java
com.couchbase.lite.ViewsTest.java
com.couchbase.lite.android.AndroidContext.java
com.couchbase.lite.android.AndroidLogger.java
com.couchbase.lite.android.AndroidNetworkReachabilityManager.java
com.couchbase.lite.android.AndroidSQLiteStorageEngineFactory.java
com.couchbase.lite.android.AndroidSQLiteStorageEngine.java
com.couchbase.lite.mockserver.MockBulkDocs.java
com.couchbase.lite.mockserver.MockChangesFeedNoResponse.java
com.couchbase.lite.mockserver.MockChangesFeed.java
com.couchbase.lite.mockserver.MockCheckpointGet.java
com.couchbase.lite.mockserver.MockCheckpointPut.java
com.couchbase.lite.mockserver.MockDispatcher.java
com.couchbase.lite.mockserver.MockDocumentBulkGet.java
com.couchbase.lite.mockserver.MockDocumentGet.java
com.couchbase.lite.mockserver.MockDocumentPut.java
com.couchbase.lite.mockserver.MockFacebookAuthPost.java
com.couchbase.lite.mockserver.MockHelper.java
com.couchbase.lite.mockserver.MockPreloadedPullTarget.java
com.couchbase.lite.mockserver.MockRevsDiff.java
com.couchbase.lite.mockserver.MockSessionGet.java
com.couchbase.lite.mockserver.SmartMockResponse.java
com.couchbase.lite.mockserver.WrappedSmartMockResponse.java
com.couchbase.lite.performance2.Test01_CreateDocs.java
com.couchbase.lite.performance2.Test02_CreateDocsUnoptimizedWay.java
com.couchbase.lite.performance2.Test03_CreateDocsWithAttachments.java
com.couchbase.lite.performance2.Test06_PullReplication.java
com.couchbase.lite.performance2.Test07_PushReplication.java
com.couchbase.lite.performance2.Test08_DocRevisions.java
com.couchbase.lite.performance2.Test09_LoadDB.java
com.couchbase.lite.performance2.Test10_DeleteDB.java
com.couchbase.lite.performance2.Test11_DeleteDocs.java
com.couchbase.lite.performance2.Test12_IndexView.java
com.couchbase.lite.performance2.Test13_QueryView.java
com.couchbase.lite.performance2.Test14_ReduceView.java
com.couchbase.lite.performance2.Test28_KeySizes.java
com.couchbase.lite.performance2.Test29_AllDocQuery.java
com.couchbase.lite.performance2.Test30_LiveQuery.java
com.couchbase.lite.performance2.Test31_CompactDB.java
com.couchbase.lite.performance.Test10_DeleteDB.java
com.couchbase.lite.performance.Test11_DeleteDocs.java
com.couchbase.lite.performance.Test12_IndexView.java
com.couchbase.lite.performance.Test13_QueryView.java
com.couchbase.lite.performance.Test14_ReduceView.java
com.couchbase.lite.performance.Test16_ParallelPushReplication.java
com.couchbase.lite.performance.Test1_CreateDocs.java
com.couchbase.lite.performance.Test2_CreateDocsUnoptimizedWay.java
com.couchbase.lite.performance.Test3_CreateDocsWithAttachments.java
com.couchbase.lite.performance.Test6_PushReplication.java
com.couchbase.lite.performance.Test7_PullReplication.java
com.couchbase.lite.performance.Test8_DocRevisions.java
com.couchbase.lite.performance.Test9_LoadDB.java
com.couchbase.lite.replicator.BulkDownloaderTest.java
com.couchbase.lite.replicator.ChangeTrackerTest.java
com.couchbase.lite.replicator.CustomizableMockHttpClient.java
com.couchbase.lite.replicator.ReplicationTest.java
com.couchbase.lite.replicator.ResponderChain.java
com.couchbase.lite.support.BatcherTest.java
com.couchbase.lite.support.JsonDocumentTest.java
com.couchbase.lite.support.PersistentCookieStoreTest.java
com.couchbase.lite.support.RemoteRequestTest.java
com.couchbase.touchdb.RevCollator.java
com.couchbase.touchdb.TDCollateJSON.java