Package org.locationtech.geogig.geotools.data.stresstest

Source Code of org.locationtech.geogig.geotools.data.stresstest.DataStoreConcurrencyTest$InsertTask

/* Copyright (c) 2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.geotools.data.stresstest;

import static com.google.common.collect.ImmutableList.copyOf;
import static org.junit.Assert.assertEquals;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.SchemaException;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.locationtech.geogig.api.Context;
import org.locationtech.geogig.api.GeoGIG;
import org.locationtech.geogig.api.RevCommit;
import org.locationtech.geogig.api.TestPlatform;
import org.locationtech.geogig.api.porcelain.ConfigOp;
import org.locationtech.geogig.api.porcelain.ConfigOp.ConfigAction;
import org.locationtech.geogig.api.porcelain.InitOp;
import org.locationtech.geogig.api.porcelain.LogOp;
import org.locationtech.geogig.cli.test.functional.general.CLITestContextBuilder;
import org.locationtech.geogig.geotools.data.GeoGigDataStore;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

public class DataStoreConcurrencyTest {

    private GeoGigDataStore store;

    private static final SimpleFeatureType pointType;
    static {
        final String pointsTypeSpec = "sp:String,ip:Integer,pp:Point:srid=4326";
        try {
            pointType = DataUtilities.createType("point", pointsTypeSpec);
        } catch (SchemaException e) {
            throw Throwables.propagate(e);
        }
    }

    private ExecutorService editThreads;

    private ExecutorService readThreads;

    private final int writeThreadCount = 4, readThreadCount = 4;

    @Rule
    public TemporaryFolder tmp = new TemporaryFolder();

    private int initialCommitCount;

    @Before
    public void beforeTest() throws Exception {

        File workingDirectory = tmp.newFolder("repo");
        File userHomeDirectory = tmp.newFolder("home");
        TestPlatform platform = new TestPlatform(workingDirectory);
        platform.setUserHome(userHomeDirectory);
        Context injector = new CLITestContextBuilder(platform).build();
        GeoGIG geogig = new GeoGIG(injector);
        geogig.command(InitOp.class).call();
        geogig.command(ConfigOp.class).setAction(ConfigAction.CONFIG_SET).setName("user.name")
                .setValue("gabriel").call();
        geogig.command(ConfigOp.class).setAction(ConfigAction.CONFIG_SET).setName("user.email")
                .setValue("gabriel@roldan.example.com").call();

        store = new GeoGigDataStore(geogig);

        store.createSchema(pointType);

        editThreads = Executors.newFixedThreadPool(writeThreadCount, new ThreadFactoryBuilder()
                .setNameFormat("edit-thread-%d").build());

        readThreads = Executors.newFixedThreadPool(readThreadCount, new ThreadFactoryBuilder()
                .setNameFormat("read-thread-%d").build());
        initialCommitCount = copyOf(store.getGeogig().command(LogOp.class).call()).size();
    }

    @After
    public void afterTest() throws Exception {
        if (store != null) {
            store.dispose();
        }
        if (editThreads != null) {
            editThreads.shutdownNow();
        }
        if (readThreads != null) {
            readThreads.shutdownNow();
        }
    }

    @Test
    public void testConcurrentEdits() throws Exception {
        final int insertsPerTask = 20;
        List<Future<Integer>> insertResults = runInserts(writeThreadCount, insertsPerTask);
        for (Future<Integer> f : insertResults) {
            assertEquals(insertsPerTask, f.get().intValue());
        }

        List<RevCommit> commits = copyOf(store.getGeogig().command(LogOp.class).call());
        final int expectedCommitCount = initialCommitCount + insertsPerTask * writeThreadCount;
        assertEquals(expectedCommitCount, commits.size());
    }

    @Test
    public void testConcurrentReads() throws Exception {

        final int insertsPerTask = 20;
        assertEquals(insertsPerTask, runInserts(1, insertsPerTask).get(0).get().intValue());

        final int readsPerTask = 20;
        List<Future<Integer>> readResults = runReads(readThreadCount, readsPerTask);

        for (Future<Integer> f : readResults) {
            assertEquals(readsPerTask, f.get().intValue());
        }
    }

    @Test
    public void testConcurrentEditsAndReads() throws Exception {

        final int insertsPerTask = 40;
        final int readsPerTask = 200;

        // have something to read
        runInserts(1, insertsPerTask).get(0).get();

        List<Future<Integer>> insertResults = runInserts(writeThreadCount, insertsPerTask);
        Thread.sleep(3000);
        List<Future<Integer>> readResults = runReads(readThreadCount, readsPerTask);

        for (Future<Integer> f : insertResults) {
            assertEquals(insertsPerTask, f.get().intValue());
        }
        for (Future<Integer> f : readResults) {
            assertEquals(readsPerTask, f.get().intValue());
        }

        List<RevCommit> commits = copyOf(store.getGeogig().command(LogOp.class).call());
        final int expectedCommitCount = insertsPerTask + initialCommitCount + insertsPerTask
                * writeThreadCount;
        assertEquals(expectedCommitCount, commits.size());
    }

    private List<Future<Integer>> runInserts(final int writeThreadCount, final int insertsPerTask) {
        List<Future<Integer>> insertResults = Lists.newArrayList();
        for (int i = 0; i < writeThreadCount; i++) {
            insertResults.add(editThreads.submit(new InsertTask(store, insertsPerTask)));
        }
        return insertResults;
    }

    private List<Future<Integer>> runReads(final int readThreadCount, final int readsPerTask) {
        List<Future<Integer>> readResults = Lists.newArrayList();
        for (int i = 0; i < readThreadCount; i++) {
            readResults.add(readThreads.submit(new ReadTask(store, readsPerTask)));
        }
        return readResults;
    }

    public static class InsertTask implements Callable<Integer> {

        private static final Random rnd = new Random(1000);

        private final GeoGigDataStore dataStore;

        private final SimpleFeatureBuilder builder;

        private int numInserts;

        public InsertTask(GeoGigDataStore store, int numInserts) {
            this.dataStore = store;
            this.numInserts = numInserts;
            this.builder = new SimpleFeatureBuilder(pointType);
        }

        @Override
        public Integer call() {
            int random;
            synchronized (rnd) {
                random = rnd.nextInt();
            }
            final String typeName = pointType.getTypeName();
            SimpleFeatureStore featureSource;
            int insertCount = 0;
            try {
                for (int i = 0; i < numInserts; i++) {
                    builder.reset();
                    builder.set("sp", String.valueOf(random));
                    builder.set("ip", Integer.valueOf(random));
                    SimpleFeature feature = builder.buildFeature(String.valueOf(random));

                    featureSource = (SimpleFeatureStore) dataStore.getFeatureSource(typeName);
                    Transaction tx = new DefaultTransaction();
                    featureSource.setTransaction(tx);
                    try {
                        featureSource.addFeatures(DataUtilities.collection(feature));
                        tx.commit();
                        insertCount++;
                    } finally {
                        tx.close();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw Throwables.propagate(e);
            }
            System.err.printf("Thread %s finished\n", Thread.currentThread().getName());
            return insertCount;
        }
    }

    public static class ReadTask implements Callable<Integer> {

        private final GeoGigDataStore dataStore;

        private final int numReads;

        public ReadTask(GeoGigDataStore store, final int numReads) {
            this.dataStore = store;
            this.numReads = numReads;
        }

        @Override
        public Integer call() {
            int readCount = 0;
            try {
                for (int i = 0; i < numReads; i++) {
                    doRead();
                    readCount++;
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw Throwables.propagate(e);
            }
            System.err.printf("Thread %s finished\n", Thread.currentThread().getName());
            return readCount;
        }

        private void doRead() throws IOException {
            final String typeName = pointType.getTypeName();
            SimpleFeatureSource featureSource;
            featureSource = dataStore.getFeatureSource(typeName);
            SimpleFeatureCollection fc = featureSource.getFeatures();
            SimpleFeatureIterator features = fc.features();
            while (features.hasNext()) {
                SimpleFeature next = features.next();
            }
            features.close();
        }
    }

    public static void main(String args[]) {
        DataStoreConcurrencyTest test = new DataStoreConcurrencyTest();
        try {
            test.tmp.create();
            test.beforeTest();
            test.testConcurrentEditsAndReads();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                test.afterTest();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
TOP

Related Classes of org.locationtech.geogig.geotools.data.stresstest.DataStoreConcurrencyTest$InsertTask

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.