Search code examples
javaorientdb

After a commited & shutdown transaction which added new class to a graph - a new Tx doesn't see the class in schema, though it is persisted


We persist a graph in a piece of code and then have another code, that tries to retrieve it. We open our transacitons with this Spring bean. Anyone who wants to access the database always calls the getGraph() method of this bean.

public class OrientDatabaseConnectionManager {
private OrientGraphFactory factory;

public OrientDatabaseConnectionManager(String path, String name, String pass) {
    factory = new OrientGraphFactory(path, name, pass).setupPool(1,10);
}

public OrientGraphFactory getFactory() {
    return factory;
}

public void setFactory(OrientGraphFactory factory) {
    this.factory = factory;
}


/**
 * Method returns graph instance from the factory's pool.
 * @return 
 */
public OrientGraph getGraph(){

    OrientGraph resultGraph = factory.getTx();
    resultGraph.setThreadMode(OrientBaseGraph.THREAD_MODE.ALWAYS_AUTOSET);
    return resultGraph;

}

}

(I was unable to quite understand the thread_mode fully, but I think it should not be related to the problem.)

The code, that persists the graph commits and shuts down, as you can see here:

OrientDatabaseConnectionManager connMan; //this is an injected bean from above.
public boolean saveGraphToOrientDB(
        SparseMultigraph<SocialVertex, SocialEdge> graph, String label) {
    boolean isSavedCorrectly = false;
    OrientGraph graphO = connMan.getGraph();
    try {
        graphDBinput.saveGraph(graph, label, graphO);
        // LOG System.out.println("Graph was saved with label "+label);
        isSavedCorrectly = true;
    } catch (AlreadyUsedGraphLabelException ex) {
        Logger.getLogger(GraphDBFacade.class.getName()).log(Level.SEVERE, null, ex);
    } finally {
        graphO.shutdown(); //calls .commit() automatically normally, but commit already happens inside.
    }
    return isSavedCorrectly;
}

This commit works well - the data are always persisted, I checked everytime in the orientdb admin interface, and the first persisted graph is always viewable okay. It might be important to note, that during the saving the label used defines new class (thus modifying schema, as I understand it) and uses it for the persisted graph.

The retrieval of the graph looks something like this:

@Override
public SocialGraph getSocialGraph(String label) {
    OrientGraph graph = connMan.getGraph();
    SocialGraph socialGraph = null;
    try {
        socialGraph = new SocialGraph(getAllSocialNodes(label, graph), getAllSocialEdges(label, graph));
    } catch (Exception e) {
        logger.error(e);
    } finally {
        graph.shutdown();
    }
    return socialGraph;
}

public List<Node> getAllSocialNodes(String label, OrientGraph graph) {
    return constructNodes(graphFilterMan.getAllNodesFromGraph(label, graph));
}

public Set<Vertex> getAllNodesFromGraph(String graphLabel, OrientGraph graph) {
    Set<Vertex> labelledGraph = new HashSet<>();
    try{
        Iterable<Vertex> configGraph = graph.getVerticesOfClass(graphLabel);
        for(Vertex v : configGraph){ //THE CODE CRASHES HERE, WITH "CLASS WITH NAME graphLabel DOES NOT EXIST
            labelledGraph.add(v);
        }
    } catch(Exception ex){
        logger.error(ex);
        graph.rollback();
    }   
    return labelledGraph;
}

So the problem is, that when we persist a new graph with a new class, say "graph01" and then we want to retrieve it, it goes okay. Later, we create a "graph02" and we want to retrieve it, but it crashes, as commented above - OrientDb tells you, that the class with "graph02" name does not exist.

It does exist in the admin interface at the time, however, when I debug, the class actually is not in the schema right after call of factory.getTx()

Right at the beginning, when we get a transaction graph instance from the factory, we get a graph with a context in which the rawGraph's underlying database's metadata have the schema proxy delegate schema shared classes WITHOUT the new class, which I can apparently see commited in the database.

Or here on picture: enter image description here

There should be one more class in the schema. The one that was persisted (and commited ) a while ago - which can also be seen in the orientDb admin interface (not present in the variable)

What I presume is happening is that the pool, from which the factory gets the transaction has somewhat cached schema or something. It does not refresh the schema, when we add a new class.

Why does the schema not show the new class, when we are trying to get the new graph out? Does schema not get refreshed?

I found here in schema documentation that

NOTE: Changes to the schema are not transactional, so execute them outside a transaction.

So should we create the new class outside a transaction and then we would get an update in the schema in the context?

//Maybe I am understanding the concepts wrong - I got in contact with OrientDb just yesterday and I am to find out the problem in an already written code.

Db we use is a remote:localhost/socialGraph OrientDB of version 1.7.4


Solution

  • In the end the problem was, that we had two liferay projects, each had its own spring application context in its WAR file and when we deployed these projects as portlets within Liferay, the two projects created two contexts, in each having one OrientDatabaseConnectionManager.

    In one context the schema was being changed. And even though I reset the connection and reloaded the schema, it only happened with the connection manager / factory in one context. The retrieving of the graph was happening in the portlet of the other project though, resulting in an outdate schema (which was not reloaded, because the reloading happened in the other spring context) - thus the error.

    So you have to be careful - either share one spring application context with beans for all your portlets (which is possible by having a parent application context, you can read more about it here)

    OR

    check for changes in the schema from within the same project which you will also use to retrieve the data later.