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

com.tinkerpop.blueprints.TransactionalGraphTestSuite Maven / Gradle / Ivy

The newest version!
package com.tinkerpop.blueprints;

import com.tinkerpop.blueprints.impls.GraphTest;
import com.tinkerpop.blueprints.util.TransactionRetryHelper;
import com.tinkerpop.blueprints.util.TransactionRetryStrategy;
import com.tinkerpop.blueprints.util.TransactionWork;
import com.tinkerpop.blueprints.util.io.graphson.GraphSONMode;
import com.tinkerpop.blueprints.util.io.graphson.GraphSONUtility;
import org.codehaus.jettison.json.JSONObject;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author Marko A. Rodriguez (http://markorodriguez.com)
 */
public class TransactionalGraphTestSuite extends TestSuite {

    public TransactionalGraphTestSuite() {
    }

    public TransactionalGraphTestSuite(final GraphTest graphTest) {
        super(graphTest);
    }

    public void testTrue() {
        assertTrue(true);
    }

    public void testRepeatedTransactionStopException() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        graph.commit();
        graph.rollback();
        graph.commit();
        graph.shutdown();
    }

    public void testAutoStartTransaction() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        Vertex v1 = graph.addVertex(null);
        vertexCount(graph, 1);
        assertEquals(v1.getId(), graph.getVertex(v1.getId()).getId());
        graph.commit();
        vertexCount(graph, 1);
        assertEquals(v1.getId(), graph.getVertex(v1.getId()).getId());
        graph.shutdown();
    }


    public void testTransactionsForVertices() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        List vin = new ArrayList();
        List vout = new ArrayList();
        vin.add(graph.addVertex(null));
        graph.commit();
        vertexCount(graph, 1);
        containsVertices(graph, vin);

        this.stopWatch();
        vout.add(graph.addVertex(null));
        vertexCount(graph, 2);
        containsVertices(graph, vin);
        containsVertices(graph, vout);
        graph.rollback();

        containsVertices(graph, vin);
        vertexCount(graph, 1);
        printPerformance(graph.toString(), 1, "vertex not added in failed transaction", this.stopWatch());

        this.stopWatch();
        vin.add(graph.addVertex(null));
        vertexCount(graph, 2);
        containsVertices(graph, vin);
        graph.commit();
        printPerformance(graph.toString(), 1, "vertex added in successful transaction", this.stopWatch());
        vertexCount(graph, 2);
        containsVertices(graph, vin);

        graph.shutdown();
    }

    public void testBasicVertexEdgeTransactions() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        Vertex v = graph.addVertex(null);
        graph.addEdge(null, v, v, graphTest.convertLabel("self"));
        assertEquals(count(v.getEdges(Direction.IN)), 1);
        assertEquals(count(v.getEdges(Direction.OUT)), 1);
        assertEquals(v.getEdges(Direction.IN).iterator().next(), v.getEdges(Direction.OUT).iterator().next());
        graph.commit();
        v = graph.getVertex(v.getId());
        assertEquals(count(v.getEdges(Direction.IN)), 1);
        assertEquals(count(v.getEdges(Direction.OUT)), 1);
        assertEquals(v.getEdges(Direction.IN).iterator().next(), v.getEdges(Direction.OUT).iterator().next());
        graph.commit();
        v = graph.getVertex(v.getId());
        assertEquals(count(v.getVertices(Direction.IN)), 1);
        assertEquals(count(v.getVertices(Direction.OUT)), 1);
        assertEquals(v.getVertices(Direction.IN).iterator().next(), v.getVertices(Direction.OUT).iterator().next());
        graph.commit();
        graph.shutdown();
    }

    public void testBruteVertexTransactions() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        List vin = new ArrayList(), vout = new ArrayList();
        this.stopWatch();
        for (int i = 0; i < 100; i++) {
            vin.add(graph.addVertex(null));
            graph.commit();
        }
        printPerformance(graph.toString(), 100, "vertices added in 100 successful transactions", this.stopWatch());
        vertexCount(graph, 100);
        containsVertices(graph, vin);

        this.stopWatch();
        for (int i = 0; i < 100; i++) {
            vout.add(graph.addVertex(null));
            graph.rollback();
        }
        printPerformance(graph.toString(), 100, "vertices not added in 100 failed transactions", this.stopWatch());

        vertexCount(graph, 100);
        containsVertices(graph, vin);
        graph.rollback();
        vertexCount(graph, 100);
        containsVertices(graph, vin);


        this.stopWatch();
        for (int i = 0; i < 100; i++) {
            vin.add(graph.addVertex(null));
        }
        vertexCount(graph, 200);
        containsVertices(graph, vin);
        graph.commit();
        printPerformance(graph.toString(), 100, "vertices added in 1 successful transactions", this.stopWatch());
        vertexCount(graph, 200);
        containsVertices(graph, vin);

        this.stopWatch();
        for (int i = 0; i < 100; i++) {
            vout.add(graph.addVertex(null));
        }
        vertexCount(graph, 300);
        containsVertices(graph, vin);
        containsVertices(graph, vout.subList(100, 200));
        graph.rollback();
        printPerformance(graph.toString(), 100, "vertices not added in 1 failed transactions", this.stopWatch());
        vertexCount(graph, 200);
        containsVertices(graph, vin);
        graph.shutdown();
    }

    public void testTransactionsForEdges() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();

        Vertex v = graph.addVertex(null);
        Vertex u = graph.addVertex(null);
        graph.commit();

        this.stopWatch();
        Edge e = graph.addEdge(null, graph.getVertex(v.getId()), graph.getVertex(u.getId()), graphTest.convertLabel("test"));


        assertEquals(graph.getVertex(v.getId()), v);
        assertEquals(graph.getVertex(u.getId()), u);
        if (graph.getFeatures().supportsEdgeRetrieval)
            assertEquals(graph.getEdge(e.getId()), e);

        vertexCount(graph, 2);
        edgeCount(graph, 1);

        graph.rollback();
        printPerformance(graph.toString(), 1, "edge not added in failed transaction (w/ iteration)", this.stopWatch());

        assertEquals(graph.getVertex(v.getId()), v);
        assertEquals(graph.getVertex(u.getId()), u);
        if (graph.getFeatures().supportsEdgeRetrieval)
            assertNull(graph.getEdge(e.getId()));

        if (graph.getFeatures().supportsVertexIteration)
            assertEquals(count(graph.getVertices()), 2);
        if (graph.getFeatures().supportsEdgeIteration)
            assertEquals(count(graph.getEdges()), 0);

        this.stopWatch();

        e = graph.addEdge(null, graph.getVertex(u.getId()), graph.getVertex(v.getId()), graphTest.convertLabel("test"));

        assertEquals(graph.getVertex(v.getId()), v);
        assertEquals(graph.getVertex(u.getId()), u);
        if (graph.getFeatures().supportsEdgeRetrieval)
            assertEquals(graph.getEdge(e.getId()), e);

        if (graph.getFeatures().supportsVertexIteration)
            assertEquals(count(graph.getVertices()), 2);
        if (graph.getFeatures().supportsEdgeIteration)
            assertEquals(count(graph.getEdges()), 1);
        assertEquals(e, getOnlyElement(graph.getVertex(u.getId()).getEdges(Direction.OUT)));
        graph.commit();
        printPerformance(graph.toString(), 1, "edge added in successful transaction (w/ iteration)", this.stopWatch());

        if (graph.getFeatures().supportsVertexIteration)
            assertEquals(count(graph.getVertices()), 2);
        if (graph.getFeatures().supportsEdgeIteration)
            assertEquals(count(graph.getEdges()), 1);

        assertEquals(graph.getVertex(v.getId()), v);
        assertEquals(graph.getVertex(u.getId()), u);
        if (graph.getFeatures().supportsEdgeRetrieval)
            assertEquals(graph.getEdge(e.getId()), e);
        assertEquals(e, getOnlyElement(graph.getVertex(u.getId()).getEdges(Direction.OUT)));

        graph.shutdown();
    }

    public void testBruteEdgeTransactions() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        this.stopWatch();
        for (int i = 0; i < 100; i++) {
            Vertex v = graph.addVertex(null);
            Vertex u = graph.addVertex(null);
            graph.addEdge(null, v, u, graphTest.convertLabel("test"));
            graph.commit();
        }
        printPerformance(graph.toString(), 100, "edges added in 100 successful transactions (2 vertices added for each edge)", this.stopWatch());
        vertexCount(graph, 200);
        edgeCount(graph, 100);

        this.stopWatch();
        for (int i = 0; i < 100; i++) {
            Vertex v = graph.addVertex(null);
            Vertex u = graph.addVertex(null);
            graph.addEdge(null, v, u, graphTest.convertLabel("test"));
            graph.rollback();
        }
        printPerformance(graph.toString(), 100, "edges not added in 100 failed transactions (2 vertices added for each edge)", this.stopWatch());
        vertexCount(graph, 200);
        edgeCount(graph, 100);

        this.stopWatch();
        for (int i = 0; i < 100; i++) {
            Vertex v = graph.addVertex(null);
            Vertex u = graph.addVertex(null);
            graph.addEdge(null, v, u, graphTest.convertLabel("test"));
        }
        vertexCount(graph, 400);
        edgeCount(graph, 200);
        graph.commit();
        printPerformance(graph.toString(), 100, "edges added in 1 successful transactions (2 vertices added for each edge)", this.stopWatch());
        vertexCount(graph, 400);
        edgeCount(graph, 200);

        this.stopWatch();
        for (int i = 0; i < 100; i++) {
            Vertex v = graph.addVertex(null);
            Vertex u = graph.addVertex(null);
            graph.addEdge(null, v, u, graphTest.convertLabel("test"));
        }
        vertexCount(graph, 600);
        edgeCount(graph, 300);

        graph.rollback();
        printPerformance(graph.toString(), 100, "edges not added in 1 failed transactions (2 vertices added for each edge)", this.stopWatch());
        vertexCount(graph, 400);
        edgeCount(graph, 200);


        graph.shutdown();
    }

    public void testPropertyTransactions() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        if (graph.getFeatures().supportsElementProperties()) {
            this.stopWatch();
            Vertex v = graph.addVertex(null);
            Object id = v.getId();
            v.setProperty("name", "marko");
            graph.commit();
            printPerformance(graph.toString(), 1, "vertex added with string property in a successful transaction", this.stopWatch());


            this.stopWatch();
            v = graph.getVertex(id);
            assertNotNull(v);
            assertEquals(v.getProperty("name"), "marko");
            v.setProperty("age", 30);
            assertEquals(v.getProperty("age"), 30);
            graph.rollback();
            printPerformance(graph.toString(), 1, "integer property not added in a failed transaction", this.stopWatch());

            this.stopWatch();
            v = graph.getVertex(id);
            assertNotNull(v);
            assertEquals(v.getProperty("name"), "marko");
            assertNull(v.getProperty("age"));
            printPerformance(graph.toString(), 2, "vertex properties checked in a successful transaction", this.stopWatch());

            Edge edge = graph.addEdge(null, v, graph.addVertex(null), "test");
            edgeCount(graph, 1);
            graph.commit();
            edgeCount(graph, 1);
            edge = getOnlyElement(graph.getVertex(v.getId()).getEdges(Direction.OUT));
            assertNotNull(edge);

            this.stopWatch();
            edge.setProperty("transaction-1", "success");
            assertEquals(edge.getProperty("transaction-1"), "success");
            graph.commit();
            printPerformance(graph.toString(), 1, "edge property added and checked in a successful transaction", this.stopWatch());
            edge = getOnlyElement(graph.getVertex(v.getId()).getEdges(Direction.OUT));
            assertEquals(edge.getProperty("transaction-1"), "success");

            this.stopWatch();
            edge.setProperty("transaction-2", "failure");
            assertEquals(edge.getProperty("transaction-1"), "success");
            assertEquals(edge.getProperty("transaction-2"), "failure");
            graph.rollback();
            printPerformance(graph.toString(), 1, "edge property added and checked in a failed transaction", this.stopWatch());
            edge = getOnlyElement(graph.getVertex(v.getId()).getEdges(Direction.OUT));
            assertEquals(edge.getProperty("transaction-1"), "success");
            assertNull(edge.getProperty("transaction-2"));
        }
        graph.shutdown();
    }

    public void testIndexTransactions() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        if (graph.getFeatures().supportsVertexIndex) {
            this.stopWatch();
            Index index = ((IndexableGraph) graph).createIndex("txIdx", Vertex.class);
            Vertex v = graph.addVertex(null);
            Object id = v.getId();
            v.setProperty("name", "marko");
            index.put("name", "marko", v);
            vertexCount(graph, 1);
            v = getOnlyElement(((IndexableGraph) graph).getIndex("txIdx", Vertex.class).get("name", "marko"));
            assertEquals(v.getId(), id);
            assertEquals(v.getProperty("name"), "marko");
            graph.commit();
            printPerformance(graph.toString(), 1, "vertex added and retrieved from index in a successful transaction", this.stopWatch());


            this.stopWatch();
            vertexCount(graph, 1);
            v = getOnlyElement(((IndexableGraph) graph).getIndex("txIdx", Vertex.class).get("name", "marko"));
            assertEquals(v.getId(), id);
            assertEquals(v.getProperty("name"), "marko");
            printPerformance(graph.toString(), 1, "vertex retrieved from index outside successful transaction", this.stopWatch());


            this.stopWatch();
            v = graph.addVertex(null);
            v.setProperty("name", "pavel");
            index.put("name", "pavel", v);
            vertexCount(graph, 2);
            v = getOnlyElement(((IndexableGraph) graph).getIndex("txIdx", Vertex.class).get("name", "marko"));
            assertEquals(v.getProperty("name"), "marko");
            v = getOnlyElement(((IndexableGraph) graph).getIndex("txIdx", Vertex.class).get("name", "pavel"));
            assertEquals(v.getProperty("name"), "pavel");
            graph.rollback();
            printPerformance(graph.toString(), 1, "vertex not added in a failed transaction", this.stopWatch());

            this.stopWatch();
            vertexCount(graph, 1);
            assertEquals(count(((IndexableGraph) graph).getIndex("txIdx", Vertex.class).get("name", "pavel")), 0);
            printPerformance(graph.toString(), 1, "vertex not retrieved in a successful transaction", this.stopWatch());
            v = getOnlyElement(((IndexableGraph) graph).getIndex("txIdx", Vertex.class).get("name", "marko"));
            assertEquals(v.getProperty("name"), "marko");
        }
        graph.shutdown();
    }

    public void testAutomaticSuccessfulTransactionOnShutdown() {

        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        if (graph.getFeatures().isPersistent && graph.getFeatures().supportsVertexProperties) {
            Vertex v = graph.addVertex(null);
            Object id = v.getId();
            v.setProperty("count", "1");
            v.setProperty("count", "2");
            graph.shutdown();
            graph = (TransactionalGraph) graphTest.generateGraph();
            Vertex reloadedV = graph.getVertex(id);
            assertEquals("2", reloadedV.getProperty("count"));

        }
        graph.shutdown();
    }

    public void testVertexCountOnPreTransactionCommit() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        Vertex v1 = graph.addVertex(null);
        graph.commit();

        vertexCount(graph, 1);

        Vertex v2 = graph.addVertex(null);
        v1 = graph.getVertex(v1.getId());
        graph.addEdge(null, v1, v2, graphTest.convertLabel("friend"));

        vertexCount(graph, 2);

        graph.commit();

        vertexCount(graph, 2);
        graph.shutdown();
    }

    public void testVertexPropertiesOnPreTransactionCommit() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        if (graph.getFeatures().supportsVertexProperties) {
            Vertex v1 = graph.addVertex(null);
            v1.setProperty("name", "marko");

            assertEquals(1, v1.getPropertyKeys().size());
            assertTrue(v1.getPropertyKeys().contains("name"));
            assertEquals("marko", v1.getProperty("name"));

            graph.commit();

            assertEquals("marko", v1.getProperty("name"));
        }
        graph.shutdown();
    }

    public void testBulkTransactionsOnEdges() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        for (int i = 0; i < 5; i++) {
            graph.addEdge(null, graph.addVertex(null), graph.addVertex(null), graphTest.convertLabel("test"));
        }
        edgeCount(graph, 5);
        graph.rollback();
        edgeCount(graph, 0);

        for (int i = 0; i < 4; i++) {
            graph.addEdge(null, graph.addVertex(null), graph.addVertex(null), graphTest.convertLabel("test"));
        }
        edgeCount(graph, 4);
        graph.rollback();
        edgeCount(graph, 0);


        for (int i = 0; i < 3; i++) {
            graph.addEdge(null, graph.addVertex(null), graph.addVertex(null), graphTest.convertLabel("test"));
        }
        edgeCount(graph, 3);
        graph.commit();
        edgeCount(graph, 3);

        graph.shutdown();
    }

    public void testCompetingThreads() {
        final TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        if (graph.getFeatures().supportsThreadIsolatedTransactions) {
            int totalThreads = 250;
            final AtomicInteger vertices = new AtomicInteger(0);
            final AtomicInteger edges = new AtomicInteger(0);
            final AtomicInteger completedThreads = new AtomicInteger(0);
            for (int i = 0; i < totalThreads; i++) {
                new Thread() {
                    public void run() {
                        Random random = new Random();
                        if (random.nextBoolean()) {
                            Vertex a = graph.addVertex(null);
                            Vertex b = graph.addVertex(null);
                            Edge e = graph.addEdge(null, a, b, graphTest.convertLabel("friend"));

                            if (graph.getFeatures().supportsElementProperties()) {
                                a.setProperty("test", this.getId());
                                b.setProperty("blah", random.nextFloat());
                                e.setProperty("bloop", random.nextInt());
                            }
                            vertices.getAndAdd(2);
                            edges.getAndAdd(1);
                            graph.commit();
                        } else {
                            Vertex a = graph.addVertex(null);
                            Vertex b = graph.addVertex(null);
                            Edge e = graph.addEdge(null, a, b, graphTest.convertLabel("friend"));
                            if (graph.getFeatures().supportsElementProperties()) {
                                a.setProperty("test", this.getId());
                                b.setProperty("blah", random.nextFloat());
                                e.setProperty("bloop", random.nextInt());
                            }
                            if (random.nextBoolean()) {
                                graph.commit();
                                vertices.getAndAdd(2);
                                edges.getAndAdd(1);
                            } else {
                                graph.rollback();
                            }
                        }
                        completedThreads.getAndAdd(1);
                    }
                }.start();
            }

            while (completedThreads.get() < totalThreads) {
            }
            assertEquals(completedThreads.get(), 250);
            edgeCount(graph, edges.get());
            vertexCount(graph, vertices.get());
        }
        graph.shutdown();
    }

    public void testCompetingThreadsOnMultipleDbInstances() throws Exception {
        // the idea behind this test is to simulate a rexster environment where two graphs of the same type
        // are being mutated by multiple threads.  the test itself surfaced issues with OrientDB in such
        // an environment and remains relevant for any graph that might be exposed through rexster.

        graphTest.dropGraph("first");
        graphTest.dropGraph("second");

        final TransactionalGraph graph1 = (TransactionalGraph) graphTest.generateGraph("first");
        final TransactionalGraph graph2 = (TransactionalGraph) graphTest.generateGraph("second");


        final Thread threadModFirstGraph = new Thread() {
            public void run() {
                final Vertex v = graph1.addVertex(null);
                // v.setProperty("name", "stephen");
                graph1.commit();
            }
        };

        threadModFirstGraph.start();
        threadModFirstGraph.join();

        final Thread threadReadBothGraphs = new Thread() {
            public void run() {
                int counter = 0;
                for (Vertex v : graph1.getVertices()) {
                    counter++;
                }

                assertEquals(1, counter);

                counter = 0;
                for (Vertex v : graph2.getVertices()) {
                    counter++;
                }

                assertEquals(0, counter);
            }
        };

        threadReadBothGraphs.start();
        threadReadBothGraphs.join();


        graph1.shutdown();
        graphTest.dropGraph("first");

        graph2.shutdown();
        graphTest.dropGraph("second");
    }

    public void testTransactionIsolationCommitCheck() throws Exception {
        // the purpose of this test is to simulate rexster access to a graph instance, where one thread modifies
        // the graph and a separate thread cannot affect the transaction of the first
        final TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();

        if (graph.getFeatures().supportsThreadIsolatedTransactions) {
            final CountDownLatch latchCommittedInOtherThread = new CountDownLatch(1);
            final CountDownLatch latchCommitInOtherThread = new CountDownLatch(1);

            // this thread starts a transaction then waits while the second thread tries to commit it.
            final Thread threadTxStarter = new Thread() {
                public void run() {
                    final Vertex v = graph.addVertex(null);

                    // System.out.println("added vertex");

                    latchCommitInOtherThread.countDown();

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

                    graph.rollback();

                    // there should be no vertices here
                    // System.out.println("reading vertex before tx");
                    assertFalse(graph.getVertices().iterator().hasNext());
                    // System.out.println("read vertex before tx");
                }
            };

            threadTxStarter.start();

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

                    // try to commit the other transaction
                    graph.commit();

                    latchCommittedInOtherThread.countDown();
                }
            };

            threadTryCommitTx.start();

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

        graph.shutdown();
    }

    public void testRemoveInTransaction() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        edgeCount(graph, 0);

        Vertex v1 = graph.addVertex(null);
        Object v1id = v1.getId();
        Vertex v2 = graph.addVertex(null);
        graph.addEdge(null, v1, v2, graphTest.convertLabel("test-edge"));
        graph.commit();

        edgeCount(graph, 1);
        Edge e1 = getOnlyElement(graph.getVertex(v1id).getEdges(Direction.OUT));
        assertNotNull(e1);
        graph.removeEdge(e1);
        edgeCount(graph, 0);
        assertNull(getOnlyElement(graph.getVertex(v1id).getEdges(Direction.OUT)));
        graph.rollback();

        edgeCount(graph, 1);
        e1 = getOnlyElement(graph.getVertex(v1id).getEdges(Direction.OUT));
        assertNotNull(e1);

        graph.removeEdge(e1);
        graph.commit();

        edgeCount(graph, 0);
        assertNull(getOnlyElement(graph.getVertex(v1id).getEdges(Direction.OUT)));
        graph.shutdown();
    }

    public void testTransactionGraphHelperFireAndForget() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();

        // first fail the tx
        new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
            @Override
            public Vertex execute(final TransactionalGraph graph) throws Exception {
                graph.addVertex(null);
                throw new Exception("fail");
            }
        }).build().fireAndForget();
        vertexCount(graph, 0);

        // this tx will work
        List vin = new ArrayList();
        TransactionRetryHelper trh = new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
            @Override
            public Vertex execute(final TransactionalGraph graph) throws Exception {
                return graph.addVertex(null);
            }
        }).build();
        vin.add(trh.fireAndForget());

        vertexCount(graph, 1);
        containsVertices(graph, vin);

        graph.shutdown();
    }

    public void testTransactionGraphHelperOneAndDone() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();

        // first fail the tx
        try {
            new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
                @Override
                public Vertex execute(final TransactionalGraph graph) throws Exception {
                    graph.addVertex(null);
                    throw new Exception("fail");
                }
            }).build().oneAndDone();
        } catch (Exception ex) {
            assertEquals("fail", ex.getCause().getMessage());
        }

        vertexCount(graph, 0);

        // this tx will work
        List vin = new ArrayList();
        vin.add(new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
            @Override
            public Vertex execute(final TransactionalGraph graph) throws Exception {
                return graph.addVertex(null);
            }
        }).build().oneAndDone());

        vertexCount(graph, 1);
        containsVertices(graph, vin);

        graph.shutdown();
    }

    public void testTransactionGraphHelperExponentialBackoff() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();

        // first fail the tx
        final AtomicInteger attempts = new AtomicInteger(0);
        try {
            new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
                @Override
                public Vertex execute(final TransactionalGraph graph) throws Exception {
                    graph.addVertex(null);
                    attempts.incrementAndGet();
                    throw new Exception("fail");
                }
            }).build().exponentialBackoff();
        } catch (Exception ex) {
            assertEquals("fail", ex.getCause().getMessage());
        }

        assertEquals(TransactionRetryStrategy.DelayedRetry.DEFAULT_TRIES, attempts.get());
        vertexCount(graph, 0);

        // this tx will work after several tries
        final AtomicInteger tries = new AtomicInteger(0);
        List vin = new ArrayList();
        vin.add(new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
            @Override
            public Vertex execute(final TransactionalGraph graph) throws Exception {
                final int tryNumber = tries.incrementAndGet();
                if (tryNumber == TransactionRetryStrategy.DelayedRetry.DEFAULT_TRIES - 2)
                    return graph.addVertex(null);
                else
                    throw new Exception("fail");
            }
        }).build().exponentialBackoff());

        vertexCount(graph, 1);
        containsVertices(graph, vin);

        graph.shutdown();
    }

    public void testTransactionGraphHelperExponentialBackoffWithExceptionChecks() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        Set exceptions = new HashSet() {{
            add(IllegalStateException.class);
        }};

        // immediately fail the tx
        final AtomicInteger attempts = new AtomicInteger(0);
        try {
            new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
                @Override
                public Vertex execute(final TransactionalGraph graph) throws Exception {
                    graph.addVertex(null);
                    attempts.incrementAndGet();
                    throw new Exception("fail");
                }
            }).build().exponentialBackoff(TransactionRetryStrategy.DelayedRetry.DEFAULT_TRIES, 20, exceptions);
        } catch (Exception ex) {
            assertEquals("fail", ex.getCause().getMessage());
        }

        assertEquals(1, attempts.get());
        vertexCount(graph, 0);

        // this tx will fail after a few tries due to exception raised not in set
        final AtomicInteger setOfTries = new AtomicInteger(0);
        try {
            new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
                @Override
                public Vertex execute(final TransactionalGraph graph) throws Exception {
                    final int tryNumber = setOfTries.incrementAndGet();
                    if (tryNumber == TransactionRetryStrategy.DelayedRetry.DEFAULT_TRIES - 2)
                        throw new Exception("fail");
                    else
                        throw new IllegalStateException("fail");
                }
            }).build().exponentialBackoff(TransactionRetryStrategy.DelayedRetry.DEFAULT_TRIES, 20, exceptions);
        } catch (Exception ex) {
            assertEquals("fail", ex.getCause().getMessage());
        }

        assertEquals(TransactionRetryStrategy.DelayedRetry.DEFAULT_TRIES - 2, setOfTries.get());
        vertexCount(graph, 0);

        // this tx will work after several tries
        final AtomicInteger tries = new AtomicInteger(0);
        List vin = new ArrayList();
        vin.add(new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
            @Override
            public Vertex execute(final TransactionalGraph graph) throws Exception {
                final int tryNumber = tries.incrementAndGet();
                if (tryNumber == TransactionRetryStrategy.DelayedRetry.DEFAULT_TRIES - 2)
                    return graph.addVertex(null);
                else
                    throw new IllegalStateException("fail");
            }
        }).build().exponentialBackoff(TransactionRetryStrategy.DelayedRetry.DEFAULT_TRIES, 20, exceptions));

        vertexCount(graph, 1);
        containsVertices(graph, vin);

        graph.shutdown();
    }

    public void testTransactionGraphHelperRetry() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();

        // first fail the tx
        final AtomicInteger attempts = new AtomicInteger(0);
        try {
            new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
                @Override
                public Vertex execute(final TransactionalGraph graph) throws Exception {
                    graph.addVertex(null);
                    attempts.incrementAndGet();
                    throw new Exception("fail");
                }
            }).build().retry();
        } catch (Exception ex) {
            assertEquals("fail", ex.getCause().getMessage());
        }

        assertEquals(TransactionRetryStrategy.DelayedRetry.DEFAULT_TRIES, attempts.get());
        vertexCount(graph, 0);

        // this tx will work after several tries
        final AtomicInteger tries = new AtomicInteger(0);
        List vin = new ArrayList();
        vin.add(new TransactionRetryHelper.Builder(graph).perform(new TransactionWork() {
            @Override
            public Vertex execute(final TransactionalGraph graph) throws Exception {
                final int tryNumber = tries.incrementAndGet();
                if (tryNumber == TransactionRetryStrategy.DelayedRetry.DEFAULT_TRIES - 2)
                    return graph.addVertex(null);
                else
                    throw new Exception("fail");
            }
        }).build().retry());

        vertexCount(graph, 1);
        containsVertices(graph, vin);

        graph.shutdown();
    }

    public void testRemovedElementsInvisibleInTransaction() {
        TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        try {
            boolean supportsVertexIteration = graph.getFeatures().supportsVertexIteration;
            boolean supportsEdgeIteration = graph.getFeatures().supportsEdgeIteration;

            if (supportsEdgeIteration) {
                assertEquals(0, count(graph.getEdges()));
            }
            Vertex v1 = graph.addVertex(null);
            Vertex v2 = graph.addVertex(null);
            Edge e1 = graph.addEdge(null, v1, v2, graphTest.convertLabel("link"));
            if (supportsVertexIteration) {
                assertEquals(2, count(graph.getVertices()));
            }
            if (supportsEdgeIteration) {
                assertEquals(1, count(graph.getEdges()));
            }
            graph.commit();
            if (supportsVertexIteration) {
                assertEquals(2, count(graph.getVertices()));
            }
            if (supportsEdgeIteration) {
                assertEquals(1, count(graph.getEdges()));
            }

            graph.removeEdge(e1);
            // We have removed an edge and not yet committed the change.
            // However, the edge should appear to be removed until the end of the transaction.
            if (supportsVertexIteration) {
                assertEquals(2, count(graph.getVertices()));
            }
            if (supportsEdgeIteration) {
                assertEquals(0, count(graph.getEdges()));
            }
            graph.removeVertex(v1);
            if (supportsVertexIteration) {
                assertEquals(1, count(graph.getVertices()));
            }
            graph.rollback();
            if (supportsVertexIteration) {
                assertEquals(2, count(graph.getVertices()));
            }
            if (supportsEdgeIteration) {
                assertEquals(1, count(graph.getEdges()));
            }
        } finally {
            graph.shutdown();
        }
    }

    public void untestSimulateRexsterIntegrationTests() throws Exception {
        // this test simulates the flow of rexster integration test.  integration tests requests are generally not made
        // in parallel, but it is expected each request they may be processed by different threads from a thread pool
        // for each request.  this test fails for orientdb given it's optimnisitc locking strategy.
        final TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        if (graph.getFeatures().supportsKeyIndices) {
            final String id = "_ID";
            ((KeyIndexableGraph) graph).createKeyIndex(id, Vertex.class);

            final int numberOfVerticesToCreate = 100;
            final Random rand = new Random(12356);
            final List graphAssignedIds = new ArrayList();

            final ExecutorService executorService = Executors.newFixedThreadPool(4);

            for (int ix = 0; ix < numberOfVerticesToCreate; ix++) {
                final int id1 = ix;
                final int id2 = ix + numberOfVerticesToCreate + rand.nextInt();

                // add a vertex and block for the thread to complete
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        final Vertex v = graph.addVertex(null);
                        v.setProperty(id, id1);
                        graph.commit();

                        graphAssignedIds.add(v.getId().toString());
                    }
                }).get();

                if (ix > 0) {
                    // add a vertex and block for the thread to complete
                    executorService.submit(new Runnable() {
                        @Override
                        public void run() {
                            final Vertex v = graph.addVertex(null);
                            v.setProperty(id, id2);
                            graph.commit();

                            graphAssignedIds.add(v.getId().toString());
                        }
                    }).get();

                    // add an edge to two randomly selected vertices and block for the thread to complete. integration
                    // tests tend to fail here, so the code is replicated pretty closely to what is in rexster
                    // (i.e. serialization to JSON) even though that may have nothing to do with failures.
                    executorService.submit(new Runnable() {
                        @Override
                        public void run() {
                            final Vertex vActual1 = graph.getVertex(graphAssignedIds.get(rand.nextInt(graphAssignedIds.size())));
                            final Vertex vActual2 = graph.getVertex(graphAssignedIds.get(rand.nextInt(graphAssignedIds.size())));
                            final Edge e = graph.addEdge(null, vActual1, vActual2, "knows");
                            e.setProperty("weight", rand.nextFloat());

                            JSONObject elementJson = null;
                            try {
                                // just replicating rexster
                                elementJson = GraphSONUtility.jsonFromElement(e, null, GraphSONMode.NORMAL);
                            } catch (Exception ex) {
                                fail();
                            }

                            graph.commit();

                            try {
                                if (elementJson != null) {
                                    // just replicating rexster
                                    elementJson.put("_ID", e.getId());
                                }
                            } catch (Exception ex) {
                                fail();
                            }
                        }
                    }).get();
                }
            }

            final Set ids = new HashSet();
            for (final Vertex v : graph.getVertices()) {
                ids.add(v.getId().toString());
            }

            for (final String idToRemove : ids) {
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        final Vertex toRemove = graph.getVertex(idToRemove);
                        graph.removeVertex(toRemove);

                        graph.commit();
                    }
                });
            }

            executorService.shutdown();
            executorService.awaitTermination(10, TimeUnit.SECONDS);
        }

        graph.shutdown();
    }

    public void untestSimulateRexsterIntegrationTestsWithRetries() throws Exception {
        // this test simulates the flow of rexster integration test. integration tests requests are generally not made
        // in parallel, but it is expected each request they may be processed by different threads from a thread pool
        // for each request...this test is similar to the previous one but includes retries.  in this case,
        // orientdb passes, but this isn't currently how Rexster integration tests work.
        final TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        if (graph.getFeatures().supportsKeyIndices && graph.getFeatures().supportsThreadIsolatedTransactions) {
            final String id = "_ID";
            ((KeyIndexableGraph) graph).createKeyIndex(id, Vertex.class);

            final int maxRetries = 10;
            final int numberOfVerticesToCreate = 100;
            final Random rand = new Random(12356);
            final List graphAssignedIds = new ArrayList();

            final ExecutorService executorService = Executors.newFixedThreadPool(4);

            for (int ix = 0; ix < numberOfVerticesToCreate; ix++) {
                final int id1 = ix;
                final int id2 = ix + numberOfVerticesToCreate + rand.nextInt();

                // add a vertex and block for the thread to complete
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        final Vertex v = graph.addVertex(null);
                        v.setProperty(id, id1);
                        graph.commit();

                        graphAssignedIds.add(v.getId().toString());
                    }
                }).get();

                if (ix > 0) {
                    // add a vertex and block for the thread to complete
                    executorService.submit(new Runnable() {
                        @Override
                        public void run() {
                            final Vertex v = graph.addVertex(null);
                            v.setProperty(id, id2);
                            graph.commit();

                            graphAssignedIds.add(v.getId().toString());
                        }
                    }).get();

                    // add an edge to two randomly selected vertices and block for the thread to complete. integration
                    // tests tend to fail here, so the code is replicated pretty closely to what is in rexster
                    // (i.e. serialization to JSON) even though that may have nothing to do with failures.
                    executorService.submit(new Runnable() {
                        @Override
                        public void run() {
                            int v1 = rand.nextInt(graphAssignedIds.size());
                            int v2 = rand.nextInt(graphAssignedIds.size());

                            for (int retry = 0; retry < maxRetries; ++retry) {
                                try {
                                    final Vertex vActual1 = graph.getVertex(graphAssignedIds.get(v1));
                                    final Vertex vActual2 = graph.getVertex(graphAssignedIds.get(v2));
                                    final Edge e = graph.addEdge(null, vActual1, vActual2, "knows");
                                    e.setProperty("weight", rand.nextFloat());

                                    // just replicating rexster
                                    final JSONObject elementJson = GraphSONUtility.jsonFromElement(e, null, GraphSONMode.NORMAL);

                                    graph.commit();

                                    if (elementJson != null) {
                                        // just replicating rexster
                                        elementJson.put("_ID", e.getId());
                                    }
                                    break;
                                } catch (Exception ex) {
                                    if (!ex.getClass().getSimpleName().equals("OConcurrentModificationException"))
                                        fail(ex.getMessage());
                                }
                            }
                        }
                    }).get();
                }
            }

            final Set ids = new HashSet();
            for (final Vertex v : graph.getVertices()) {
                ids.add(v.getId().toString());
            }

            for (final String idToRemove : ids) {
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        final Vertex toRemove = graph.getVertex(idToRemove);
                        graph.removeVertex(toRemove);

                        graph.commit();
                    }
                });
            }

            executorService.shutdown();
            executorService.awaitTermination(10, TimeUnit.SECONDS);
        }

        graph.shutdown();
    }

    public void untestTransactionVertexPropertiesAcrossThreads() throws Exception {
        // the purpose of this test is to ensure that properties of a element are available prior to commit()
        // across threads
        final TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        if (graph.getFeatures().supportsThreadIsolatedTransactions) {
            final AtomicReference v = new AtomicReference();
            final Thread thread = new Thread() {
                public void run() {
                    final Vertex vertex = graph.addVertex(null);
                    vertex.setProperty("name", "stephen");
                    v.set(vertex);
                }
            };

            thread.start();
            thread.join();

            Set k = v.get().getPropertyKeys();
            assertTrue(k.contains("name"));
            assertEquals("stephen", v.get().getProperty("name"));
        }
        graph.shutdown();
    }

    public void untestTransactionIsolationWithSeparateThreads() throws Exception {
        // the purpose of this test is to simulate rexster access to a graph instance, where one thread modifies
        // the graph and a separate thread reads before the transaction is committed.  the expectation is that
        // the changes in the transaction are isolated to the thread that made the change and the second thread
        // should not see the change until commit() in the first thread.
        final TransactionalGraph graph = (TransactionalGraph) graphTest.generateGraph();
        if (graph.getFeatures().supportsThreadIsolatedTransactions) {
            final CountDownLatch latchCommit = new CountDownLatch(1);
            final CountDownLatch latchFirstRead = new CountDownLatch(1);
            final CountDownLatch latchSecondRead = new CountDownLatch(1);

            final Thread threadMod = new Thread() {
                public void run() {
                    final Vertex v = graph.addVertex(null);
                    //v.setProperty("name", "stephen");

                    // System.out.println("added vertex");

                    latchFirstRead.countDown();

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

                    graph.commit();

                    // System.out.println("committed vertex");

                    latchSecondRead.countDown();
                }
            };

            threadMod.start();

            final Thread threadRead = new Thread() {
                public void run() {
                    try {
                        latchFirstRead.await();
                    } catch (InterruptedException ie) {
                        throw new RuntimeException(ie);
                    }

                    // System.out.println("reading vertex before tx");
                    assertFalse(graph.getVertices().iterator().hasNext());
                    // System.out.println("read vertex before tx");

                    latchCommit.countDown();

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

                    // System.out.println("reading vertex after tx");
                    assertTrue(graph.getVertices().iterator().hasNext());
                    // System.out.println("read vertex after tx");
                }
            };

            threadRead.start();

            threadMod.join();
            threadRead.join();
        }

        graph.shutdown();

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy