All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.umlg.sqlg.test.schema.TestMultiThread Maven / Gradle / Ivy

There is a newer version: 3.1.1
Show newest version
package org.umlg.sqlg.test.schema;

import com.google.common.base.Preconditions;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.umlg.sqlg.structure.PropertyType;
import org.umlg.sqlg.structure.SqlgGraph;
import org.umlg.sqlg.structure.topology.VertexLabel;
import org.umlg.sqlg.test.BaseTest;

import java.net.URL;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

/**
 * Date: 2014/09/24
 * Time: 10:46 AM
 */
public class TestMultiThread extends BaseTest {

    private final Logger logger = LoggerFactory.getLogger(TestMultiThread.class.getName());

    /**
     * This test is a duplicate of TransactionTest.shouldSupportTransactionIsolationCommitCheck but with the schema created upfront else it deadlocks.
     */
    @Test
    public void shouldSupportTransactionIsolationCommitCheck() throws Exception {
        Vertex v1 = this.sqlgGraph.addVertex();
        this.sqlgGraph.tx().commit();
        v1.remove();
        this.sqlgGraph.tx().commit();
        // the purpose of this test is to simulate gremlin server access to a graph instance, where one thread modifies
        // the graph and a separate thread cannot affect the transaction of the first
        final CountDownLatch latchCommittedInOtherThread = new CountDownLatch(1);
        final CountDownLatch latchCommitInOtherThread = new CountDownLatch(1);

        final AtomicBoolean noVerticesInFirstThread = new AtomicBoolean(false);

        // this thread starts a transaction then waits while the second thread tries to commit it.
        final Thread threadTxStarter = new Thread("thread1") {
            @Override
            public void run() {
                TestMultiThread.this.sqlgGraph.addVertex();
                latchCommitInOtherThread.countDown();

                try {
                    latchCommittedInOtherThread.await();
                } catch (InterruptedException ie) {
                    throw new RuntimeException(ie);
                }

                TestMultiThread.this.sqlgGraph.tx().rollback();

                // there should be no vertices here
                noVerticesInFirstThread.set(!TestMultiThread.this.sqlgGraph.vertices().hasNext());
            }
        };

        threadTxStarter.start();

        // this thread tries to commit the transaction started in the first thread above.
        final Thread threadTryCommitTx = new Thread("thread2") {
            @Override
            public void run() {
                try {
                    latchCommitInOtherThread.await();
                } catch (InterruptedException ie) {
                    throw new RuntimeException(ie);
                }

                // try to commit the other transaction
                TestMultiThread.this.sqlgGraph.tx().commit();

                latchCommittedInOtherThread.countDown();
            }
        };

        threadTryCommitTx.start();

        threadTxStarter.join();
        threadTryCommitTx.join();

        Assert.assertTrue(noVerticesInFirstThread.get());
        assertVertexEdgeCounts(sqlgGraph, 0, 0);
    }

    @Test
    public void shouldExecuteWithCompetingThreads() throws InterruptedException {
        //Create the schema upfront so that graphs (Hsqldb, H2, Mysql...) that do not support transactional schema's can succeed.
        VertexLabel vertexLabel = this.sqlgGraph.getTopology().ensureVertexLabelExist("vertex", new HashMap() {{
            put("test", PropertyType.LONG);
            put("blah", PropertyType.DOUBLE);
        }});
        vertexLabel.ensureEdgeLabelExist("friend", vertexLabel, new HashMap() {{
            put("bloop", PropertyType.INTEGER);
        }});
        this.sqlgGraph.tx().commit();
        final Graph graph = this.sqlgGraph;
        int totalThreads = 250;
        final AtomicInteger vertices = new AtomicInteger(0);
        final AtomicInteger edges = new AtomicInteger(0);
        final AtomicInteger completedThreads = new AtomicInteger(0);
        CountDownLatch countDownLatch = new CountDownLatch(totalThreads);
        for (int i = 0; i < totalThreads; i++) {
            new Thread() {
                @Override
                public void run() {
                    try {
                        final Random random = new Random();
                        if (random.nextBoolean()) {
                            final Vertex a = graph.addVertex();
                            final Vertex b = graph.addVertex();
                            final Edge e = a.addEdge("friend", b);

                            vertices.getAndAdd(2);
                            a.property("test", this.getId());
                            b.property("blah", random.nextDouble());
                            e.property("bloop", random.nextInt());
                            edges.getAndAdd(1);
                            graph.tx().commit();
                        } else {
                            final Vertex a = graph.addVertex();
                            final Vertex b = graph.addVertex();
                            final Edge e = a.addEdge("friend", b);

                            a.property("test", this.getId());
                            b.property("blah", random.nextDouble());
                            e.property("bloop", random.nextInt());

                            if (random.nextBoolean()) {
                                graph.tx().commit();
                                vertices.getAndAdd(2);
                                edges.getAndAdd(1);
                            } else {
                                graph.tx().rollback();
                            }
                        }
                        completedThreads.getAndAdd(1);
                        logger.info("shouldExecuteWithCompetingThreads " + completedThreads.get());

                    } catch (Exception e) {
                        logger.error("failure", e);
                        Assert.fail(e.getMessage());
                    } finally {
                        countDownLatch.countDown();
                    }
                }
            }.start();
        }
        boolean success = countDownLatch.await(5, TimeUnit.MINUTES);
        Assert.assertTrue(success);
        Assert.assertEquals(completedThreads.get(), totalThreads);
        System.out.println(vertices.get());
        assertVertexEdgeCounts(graph, vertices.get(), edges.get());
    }

    private static void assertVertexEdgeCounts(final Graph graph, final int expectedVertexCount, final int expectedEdgeCount) {
        getAssertVertexEdgeCounts(expectedVertexCount, expectedEdgeCount).accept(graph);
    }

    private static Consumer getAssertVertexEdgeCounts(final int expectedVertexCount, final int expectedEdgeCount) {
        return (g) -> {
            Assert.assertEquals(expectedVertexCount, IteratorUtils.count(g.vertices()));
            Assert.assertEquals(expectedEdgeCount, IteratorUtils.count(g.edges()));
        };
    }

    @Test
    public void testMultiThreadVertices() throws InterruptedException {
        Set tables = new ConcurrentSkipListSet<>();
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int j = 0; j < 20; j++) {
            int finalJ = j;
            executorService.submit(() -> {
                try {
                    final Random random = new Random();
                    int randomInt = random.nextInt();
                    for (int i = 0; i < 10; i++) {
                        sqlgGraph.addVertex(T.label, "Person" + finalJ, "name", String.valueOf(finalJ));
                    }
                    sqlgGraph.tx().commit();
                    tables.add(finalJ);
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                    Assert.fail(e.getMessage());
                    sqlgGraph.tx().rollback();
                }
            });
        }
        executorService.shutdown();
        if (executorService.awaitTermination(6000, TimeUnit.SECONDS)) {
            logger.info("normal termination");
        } else {
            Assert.fail("failed to terminate executor service normally");
        }
        for (Integer i : tables) {
            logger.info(String.format("looking for 'Person%d'", i));
            Assert.assertTrue(String.format("Person%d not found", i), this.sqlgGraph.getTopology().getVertexLabel(this.sqlgGraph.getSqlDialect().getPublicSchema(), "Person" + i).isPresent());
            Assert.assertEquals(10, this.sqlgGraph.traversal().V().has(T.label, "Person" + i).has("name", String.valueOf(i)).count().next().intValue());
        }
    }

    @Test
    public void testMultiThreadEdges() throws InterruptedException {
        //For some reason Maria don't like this one on teamcity
        Assume.assumeFalse(isMariaDb());
        Vertex v1 = sqlgGraph.addVertex(T.label, "Person", "name", "0");
        sqlgGraph.tx().commit();
        Set tables = new ConcurrentSkipListSet<>();
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int j = 0; j < 100; j++) {
            executorService.submit(() -> {
                final Random random = new Random();
                int randomInt = random.nextInt();
                for (int i = 0; i < 10; i++) {
                    Vertex v2 = sqlgGraph.addVertex(T.label, "Person" + String.valueOf(randomInt), "name", String.valueOf(randomInt));
                    v1.addEdge("test" + String.valueOf(randomInt), v2);
                    tables.add(randomInt);
                }
                sqlgGraph.tx().commit();
            });
        }
        executorService.shutdown();
        boolean success = executorService.awaitTermination(60, TimeUnit.SECONDS);
        Assert.assertTrue(success);
        for (Integer i : tables) {
            Assert.assertTrue(this.sqlgGraph.getTopology().getVertexLabel(this.sqlgGraph.getSqlDialect().getPublicSchema(), "Person" + i).isPresent());
            Assert.assertEquals(10, this.sqlgGraph.traversal().V().has(T.label, "Person" + i).has("name", String.valueOf(i)).count().next().intValue());
            Assert.assertEquals(10, vertexTraversal(this.sqlgGraph, v1).out("test" + i).count().next().intValue());
        }
    }

    @Test
    public void testMultiThreadCreateSchemas() throws InterruptedException, ExecutionException {
        Set schemas = new HashSet<>();
        ExecutorService executorService = Executors.newFixedThreadPool(200);
        for (int i = 0; i < 10_000; i++) {
            Integer schema = new Random().nextInt(99);
            schemas.add(schema);
            Future f = executorService.submit(() -> {
                this.sqlgGraph.getTopology().ensureSchemaExist("schema_" + schema);
                this.sqlgGraph.tx().commit();
            });
            f.get();
        }
        executorService.shutdown();
        boolean terminated = executorService.awaitTermination(5, TimeUnit.SECONDS);
        Assert.assertTrue(terminated);
        //+ 1 for the public schema
        Assert.assertEquals(schemas.size() + 1, this.sqlgGraph.getTopology().getSchemas().size());
    }

    /**
     * test when each graph is created in its own thread, in distributed mode
     */
    @Test
    public void testMultipleGraphs() throws Exception {
        URL sqlProperties = Thread.currentThread().getContextClassLoader().getResource("sqlg.properties");
        try {
            configuration = new PropertiesConfiguration(sqlProperties);
            Assume.assumeTrue(isPostgres());
            configuration.addProperty("distributed", true);
            if (!configuration.containsKey("jdbc.url"))
                throw new IllegalArgumentException(String.format("SqlGraph configuration requires that the %s be set", "jdbc.url"));

        } catch (ConfigurationException e) {
            throw new RuntimeException(e);
        }
        VertexLabel personTrue = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Person_True", new LinkedHashMap<>() {{
            put("name", PropertyType.STRING);
        }});
        VertexLabel addressTrue = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Address_True", new LinkedHashMap<>() {{
            put("name", PropertyType.STRING);
        }});
        personTrue.ensureEdgeLabelExist("address_True", addressTrue, new LinkedHashMap<>() {{
            put("name", PropertyType.STRING);
        }});
        VertexLabel personLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Person", new LinkedHashMap<>() {{
            put("name", PropertyType.STRING);
        }});
        VertexLabel addressLabel = this.sqlgGraph.getTopology().getPublicSchema().ensureVertexLabelExist("Address", new LinkedHashMap<>() {{
            put("name", PropertyType.STRING);
        }});
        personLabel.ensureEdgeLabelExist("address", addressLabel, new LinkedHashMap<>() {{
            put("name", PropertyType.STRING);
        }});
        this.sqlgGraph.tx().commit();
        ExecutorService executorService = Executors.newFixedThreadPool(50);
        int loop = 400;
        for (int i = 0; i < loop; i++) {
            String n = "person" + i;
            executorService.submit(() -> {
                try {
                    try (SqlgGraph sqlgGraph1 = SqlgGraph.open(configuration)) {
                        final Random random = new Random();
                        if (random.nextBoolean()) {
                            Vertex person = sqlgGraph1.addVertex(T.label, "Person_True", "name", n);
                            Vertex address = sqlgGraph1.addVertex(T.label, "Address_True", "name", n);
                            person.addEdge("address_True", address, "name", n);
                        } else {
                            Vertex person = sqlgGraph1.addVertex(T.label, "Person", "name", n);
                            Vertex address = sqlgGraph1.addVertex(T.label, "Address", "name", n);
                            person.addEdge("address", address, "name", n);
                        }
                        sqlgGraph1.tx().commit();
                    }
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                    Assert.fail(e.getMessage());
                }
            });
        }
        executorService.shutdown();
        boolean terminated = executorService.awaitTermination(100, TimeUnit.SECONDS);
        Assert.assertTrue(terminated);
        try (SqlgGraph sqlgGraph1 = SqlgGraph.open(configuration)) {
            Assert.assertEquals(400, sqlgGraph1.traversal().V().hasLabel("Person_True").count().next() + sqlgGraph1.traversal().V().hasLabel("Person").count().next());
            Assert.assertEquals(400, sqlgGraph1.traversal().V().hasLabel("Address_True").count().next() + sqlgGraph1.traversal().V().hasLabel("Address").count().next());
            Assert.assertEquals(400, sqlgGraph1.traversal().E().hasLabel("address_True").count().next() + sqlgGraph1.traversal().E().hasLabel("address").count().next());
        }
    }

    /**
     * test when each graph is created in its own thread, in distributed mode
     * each thread created a different label
     *
     * @throws Exception
     */
    @Test
    public void testMultipleGraphsMultipleLabels() throws Exception {
        URL sqlProperties = Thread.currentThread().getContextClassLoader().getResource("sqlg.properties");
        try {
            configuration = new PropertiesConfiguration(sqlProperties);
            Assume.assumeTrue(isPostgres());
            configuration.addProperty("distributed", true);
            configuration.addProperty("maxPoolSize", 3);
            if (!configuration.containsKey("jdbc.url"))
                throw new IllegalArgumentException(String.format("SqlGraph configuration requires that the %s be set", "jdbc.url"));

        } catch (ConfigurationException e) {
            Assert.fail(e.getMessage());
        }

        ExecutorService executorService = Executors.newFixedThreadPool(200);
        int loop = 200;
//        int loop = 2;
        for (int i = 0; i < loop; i++) {
            String n = "person" + i;
            executorService.submit(() -> {
                try {
                    try (SqlgGraph sqlgGraph2 = SqlgGraph.open(configuration)) {
                        sqlgGraph2.addVertex(T.label, "Person" + n, "name", n);
                        sqlgGraph2.tx().commit();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Assert.fail(e.getMessage());
                }
            });
        }
        executorService.shutdown();
        boolean terminatedNormally = executorService.awaitTermination(1, TimeUnit.MINUTES);
        Preconditions.checkState(terminatedNormally);

        try (SqlgGraph sqlgGraph2 = SqlgGraph.open(configuration)) {
            for (int i = 0; i < loop; i++) {
                String n = "person" + i;
                Assert.assertEquals(1, sqlgGraph2.traversal().V().hasLabel("Person" + n).count().next().longValue());
            }
        }
    }

    @Test
    public void testLoadsOfSchemaChanges() throws InterruptedException, ExecutionException {
        Assume.assumeFalse(isH2());
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        List> futureList = new ArrayList<>();
        int loop = 1000;
        for (int i = 0; i < loop; i++) {
            String n = "person" + i;
            String edge = "e" + i;
            int current = i;
            futureList.add(executorService.submit(() -> {
                try {
                    Vertex v1 = this.sqlgGraph.addVertex(T.label, n, "name", n);
                    Vertex v2 = this.sqlgGraph.addVertex(T.label, n, "name", n);
                    final Random random = new Random();
                    if (random.nextBoolean()) {
                        v1.property("another" + n, "asd");
                    }
                    if (random.nextBoolean()) {
                        Edge e = v1.addEdge(edge, v2);
                        if (random.nextBoolean()) {
                            e.property("yetanother" + n, "asd");
                        }
                    }
                    this.sqlgGraph.tx().commit();
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                    Assert.fail(e.getMessage());
                }
                return current;
            }));
        }
        executorService.shutdown();
        for (Future future : futureList) {
            logger.info("Completed " + future.get());
        }
        boolean terminated = executorService.awaitTermination(1, TimeUnit.SECONDS);
        Preconditions.checkState(terminated, "executorService terminated via timeout");
        for (int i = 0; i < loop; i++) {
            String n = "person" + i;
            Assert.assertEquals(n + " failed", 2, this.sqlgGraph.traversal().V().hasLabel(n).count().next().longValue());
        }
    }

    @Test
    public void simulateReadWriteChange() throws ExecutionException, InterruptedException {
        //Sleep here, help with testing connections from previous test staying idle on postgres.
        Thread.sleep(3_000);
        List labels = new ArrayList<>();
        List properties = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            labels.add("label" + i);
            properties.add("property" + i);
        }
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        List> futureList = new ArrayList<>();

        for (int i = 0; i < 3; i++) {
            int count = i;
            futureList.add(executorService.submit(() -> {
                try {
                    for (int i1 = 0; i1 < 1000; i1++) {
                        sqlgGraph.addVertex(T.label, labels.get(i1), properties.get(i1), "asd");
                        sqlgGraph.tx().commit();
                    }
                    return count;
                } catch (Exception e) {
                    sqlgGraph.tx().rollback();
                    throw new RuntimeException(e);
                }
            }));
        }
        for (int i = 0; i < 3; i++) {
            int count = i;
            futureList.add(executorService.submit(() -> {
                try {
                    for (int i12 = 0; i12 < 1000; i12++) {
                        sqlgGraph.traversal().V().hasLabel(labels.get(i12)).iterate();
                        sqlgGraph.tx().rollback();
                    }
                    return count;
                } catch (Exception e) {
                    sqlgGraph.tx().rollback();
                    throw new RuntimeException(e);
                }
            }));
        }
        executorService.shutdown();
        for (Future integerFuture : futureList) {
            logger.info("done " + integerFuture.get());
        }
        Assert.assertEquals(1000, this.sqlgGraph.getTopology().getPublicSchema().getVertexLabels().size());
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy