Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error of transitioning to 1.7-rc1 from 1.6.3 #2114

Closed
andrii0lomakin opened this issue Mar 11, 2014 · 15 comments
Closed

Error of transitioning to 1.7-rc1 from 1.6.3 #2114

andrii0lomakin opened this issue Mar 11, 2014 · 15 comments
Assignees
Milestone

Comments

@andrii0lomakin
Copy link
Member

Andrey,

I identified the issue in the previous test code I sent to you. In order to create vertex properties of type LINK or LINKSET I have to use the native API. In the following code, I simulate what it is that I am trying to do that is running into issues. Every time a new record is added (or a group of records) we are trying to update an index we maintain. The index is stored in the form of a class that has two properties, a key and a value which is a set of record pointers that have this key value. We are trying to update this index using a separate thread so that we can decouple the original request to create the record from the internal index update. The problem is that the record pointer is a temporary ID as opposed to a persisted one so that later one when we try to use the index, the records are not found in the database. Do you have any suggestions on what we are doing wrong or how to fix it?

Thanks,
Odysseas

public class TestOrientDbConcurrency extends TestCase
{
private final static String DB_URL = "plocal://mnt/sysnet/person-db";
private static final int THREAD_COUNT = 1;

public void testDirtyTxQuery() throws Exception {
    OrientGraph graph = new OrientGraph(DB_URL);

// OrientVertexType personType = graph.createVertexType("persons");
// OrientVertexType addressType = graph.createVertexType("addresses");

    final AtomicBoolean shutdownFlag = new AtomicBoolean(false);

    final ExecutorService executorService = Executors.newCachedThreadPool();

    List<Future> futures = new ArrayList<Future>();

    for (int j=0; j < THREAD_COUNT; j++) {
        final Future inserter = executorService.submit(new Callable<Void>()
        {

            @Override
            public Void call() throws Exception {
                OrientGraph graph = new OrientGraph(DB_URL);

                int counter = 0;
                graph.getRawGraph().begin();
                while (!shutdownFlag.get()) {
                    OrientVertex vertex = graph.addVertex("class:persons");
                    vertex.setProperty("firstName", "John"+counter);
                    vertex.setProperty("lastName", "Orientdb" + counter);
                    Set<OrientVertex> addresses = new HashSet<OrientVertex>();
                    for (int i=0; i < 5; i++) {
                        OrientVertex aVertex = graph.addVertex("class:addresses");
                        aVertex.setProperty("city", "Baltimore");
                        aVertex.getRecord().field("person", vertex, com.orientechnologies.orient.core.metadata.schema.OType.LINK);
                        addresses.add(aVertex);
                    }
                    vertex.getRecord().field("addresses", addresses, com.orientechnologies.orient.core.metadata.schema.OType.LINKSET);
                    counter++;
                    executorService.submit(new BlockingUpdate("John", vertex.getIdentity().toString()));
                    if (counter % 100 == 0) {
                        System.out.println("Saved 100 records by thread: " + Thread.currentThread().getName());
                        graph.commit();
                    }
                }
                graph.commit();
                return null;
            }
        });
        futures.add(inserter);
    }

    final Future fetcher = executorService.submit(new Callable<Void>()
    {
        @Override
        public Void call() throws Exception {
            OrientGraph graph = new OrientGraph(DB_URL);

            while (!shutdownFlag.get())
                graph.command(new OCommandSQL("select count(*) from persons")).execute();

            return null;
        }
    });

    Thread.sleep(30000);

    shutdownFlag.set(true);

    for (Future future : futures) {
        future.get();
    }
    fetcher.get();
}

public class BlockingUpdate implements Callable<Void> {
    private String blockingKeyValue;
    private String rid;

    public BlockingUpdate(String blockingKeyValue, String rid) {
        super();
        this.blockingKeyValue = blockingKeyValue;
        this.rid = rid;
    }

    @Override
    public Void call() {
        try {
            OrientGraph graph = new OrientGraph(DB_URL);
            graph.getRawGraph().begin();
            List<ODocument> docs = graph.getRawGraph().command(new OCommandSQL("select rids from Blockinground-0 where blockingKeyValue = " + blockingKeyValue)).execute();
            if (docs.size() == 0) {
                Set<String> rids = new HashSet<String>();
                rids.add(rid);
                OrientVertex vertex = graph.addVertex("class:Blockinground-0");
                vertex.setProperty("blockingKeyValue", blockingKeyValue);
                vertex.getRecord().field("rids", rids, OType.EMBEDDEDSET);
                vertex.getRecord().save();
                System.out.println("Vertex is now " + vertex);
            } else {
                ODocument doc = docs.get(0);
                Set<String> rids = doc.field("rids");
                rids.add(rid);
                doc.save();
                System.out.println("Doc is now " + doc);
            }

            graph.commit();
        } catch (Exception e) {
            System.err.println("Got an exception: " + e);
        }
        return null;
    }
}

}

@andrii0lomakin andrii0lomakin added this to the 1.7rc2 milestone Mar 11, 2014
@andrii0lomakin andrii0lomakin self-assigned this Mar 11, 2014
@andrii0lomakin
Copy link
Member Author

Odyseas,
I just looked into your issue but it is simply impossible ot access records ids before commit.
If it is running in separate thread is it possible to run this thread after commit ?

@odysseaspenta
Copy link

Andrey,
In our actual code, we do a commit and then generate the notification message which in a separate thread updates the index information yet we still on occasion get a message that it was unable to load a record with id 11:-2. It has been hard to track down the issue because it appears to be a race condition which happens once in a while. What you are saying is though that as long as we do a commit then upon return from the commit, the record should have a permanent ID?

@andrii0lomakin
Copy link
Member Author

Hi, yes,
But please note that id which can be passed in thread may be not updated because it is not thread safe. so thread should be started with ids only after commit.
Did you do in such way ?

@odysseaspenta
Copy link

Andrey,
We are making the assumption that after the commit, the ODocument will have the IDs set to permanent values instead of temporary IDs. The thread will be started with a reference to the ODocument structure after the commit.
I am not sure I understand what do you mean by the "id which can be passed in thread may not be updated".
Since the structure we are creating has references across multiple documents is there a more efficient way we can persist the data safely or do we need to have three commits?

  1. Commit of the parent document
  2. Commit of the child documents with a reference to the parent
  3. Commit after update to the parent document with property of type LINKSET with all the child odcuments.
    Thanks

@andrii0lomakin
Copy link
Member Author

Yes, you are right.
RIDs in commit are persistent.

You do not need 3 commits if you have links between records and these records are not stored, you should pass records itself.
They will be converted in valid links after commit.

Why you do not use edges but LINKSET ?

@odysseaspenta
Copy link

Andrey,
We used LINKSET because we thought that would be more efficient than creating edges to documents. We started this approach when there were no lightweight edges so maybe our rational no longer applies. Do you suggest that we shift over to using edges to link the parent object to the child objects?

@andrii0lomakin
Copy link
Member Author

It should be more efficient if you can afford to spend time to migrate to this approach.

@odysseaspenta
Copy link

It was a lot of work to migrate from the OGraphDatabase APi to use a mixture of Blueprint and raw OrientDB APi so changing how we store this association is not too bad if it will be more efficient. One advantage of using edges is that we won't have to build forward and backward links to the data manually. Thanks, Odysseas

@odysseaspenta
Copy link

I am working on converting the use of a LINKSET into using a edges from the parent node to all the children nodes. As you suggested I am trying to achieve this within one transaction but I am running into the same type of exception again (see the exception below). The exception comes up while trying to save the out link on the document:

public OrientEdge addEdge(String label, final OrientVertex inVertex, final String iClassName, final String iClusterName,
final Object... fields) {

...
// OUT-VERTEX ---> IN-VERTEX/EDGE
createLink(outDocument, to, outFieldName);

// IN-VERTEX ---> OUT-VERTEX/EDGE
createLink(inDocument, from, inFieldName);

edge.save(iClusterName);
inDocument.save();
outDocument.save(); // <----     EXCEPTION OCCURS HERE

return edge;

}

[2014-03-11 20:27:34,477]ERROR 53764[taskExecutorFileLoaderMap-3] - org.openhie.openempi.entity.dao.orientdb.EntityDaoOrientdb.saveRecords(EntityDaoOrientdb.java:127) - Failed while trying to save a set of records of entity: person due to com.orientechnologies.orient.core.index.OIndexException: Error during loading of record with id : #11:-2
com.orientechnologies.orient.core.index.OIndexException: Error during loading of record with id : #11:-2
at com.orientechnologies.orient.core.index.OClassIndexManager.checkForLoading(OClassIndexManager.java:549)
at com.orientechnologies.orient.core.index.OClassIndexManager.checkIndexes(OClassIndexManager.java:485)
at com.orientechnologies.orient.core.index.OClassIndexManager.onRecordBeforeUpdate(OClassIndexManager.java:119)
at com.orientechnologies.orient.core.hook.ODocumentHookAbstract.onTrigger(ODocumentHookAbstract.java:263)
at com.orientechnologies.orient.core.db.record.ODatabaseRecordAbstract.callbackHooks(ODatabaseRecordAbstract.java:1143)
at com.orientechnologies.orient.core.tx.OTransactionOptimistic.addRecord(OTransactionOptimistic.java:329)
at com.orientechnologies.orient.core.tx.OTransactionOptimistic.saveRecord(OTransactionOptimistic.java:310)
at com.orientechnologies.orient.core.db.record.ODatabaseRecordTx.save(ODatabaseRecordTx.java:274)
at com.orientechnologies.orient.core.db.record.ODatabaseRecordAbstract.save(ODatabaseRecordAbstract.java:1)
at com.orientechnologies.orient.core.record.ORecordAbstract.save(ORecordAbstract.java:329)
at com.orientechnologies.orient.core.record.impl.ODocument.save(ODocument.java:1358)
at com.orientechnologies.orient.core.record.impl.ODocument.save(ODocument.java:1347)
at com.orientechnologies.orient.core.record.impl.ODocument.save(ODocument.java:1336)
at com.tinkerpop.blueprints.impls.orient.OrientVertex.addEdge(OrientVertex.java:258)
at com.tinkerpop.blueprints.impls.orient.OrientVertex.addEdge(OrientVertex.java:183)

@odysseaspenta
Copy link

Andrey,
I found that if I turn off the mechanism that updates the index every time new records are added, then I don't get the errors shown above whereas if I turn it on, they occur once in a while. I need to investigate further to see what is causing this.
Odysseass

@odysseaspenta
Copy link

Andrey,
After the commit, is it true that the following will always be false?
doc.getIdentity().isTemporary()

@andrii0lomakin
Copy link
Member Author

Yes, you have found out issue ?

@odysseaspenta
Copy link

I am finding that after the commit I am still getting some temporary ids but I want to confirm this before I waste your time. I will let you know if I confirm it. Thanks.

@andrii0lomakin
Copy link
Member Author

We fixed similar in 1.7-rc2 snapshot according to reports it is already pretty stable could you try it ?

@odysseaspenta
Copy link

Good news Andrey. With 1.7-rc2 I am not having the problem any more. I switched to using edges instead of LINKSETs to store the one to many association and I see that it is automatically using lightweight edges to store the associations. Thanks for your help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants