Experience with OrientDB so far

2,705 views
Skip to first unread message

Edwin Dalorzo

unread,
Jul 31, 2013, 1:28:59 PM7/31/13
to orient-...@googlegroups.com
I have spent the past month developing an API for my company using OrientDB. The initial scenario was interesting, because I was given an already implemented API using Neo4j and Spring Data and my responsibility was to turn it into an API working with OrientDB to see how that would work. Our purpose was to evaluate OrientDB and see if it was a good candidate to replace Neo4j.

To ensure we could always go back, I implemented the new API using Tinkerpop Blueprints and Gremlin.

Paradoxes of Open Source: #1 - The Unstable API


The first problem is an interesting paradox. 

  • The latest release of OrientDB is 1.5.0 (just released yesterday).
  • The creators of the database say that the official API to work with the database is the Tinkerpop stack.
  •  The latest production-ready release of the Tinkerpop stack is 2.3.0.
  • However, the Tinkerpop release 2.3.0 release does not work with OrientDB 1.4.0, 1.4.1, nor 1.5.0. (it causes exceptions).
  • So, there is no way to use OrientDB 1.4.0, 1.4.1 or 1.5.0 with a stable, production-ready API?

 Now, since we are planning to deploy in production soon, I thought, well, in that case I will use OrientDB 1.3.0. This is one of the releases appearing on the release page, so I assumed it was still supported.

Using OrientDB 1.3.0 and Bluprints and Gremlin 2.3.0 I developed the entire API and I worked just fine. In fact, I felt it was significantly faster than when running agains Neo4j.

Then I moved into evaluating how to implement replication and there it is when I started running into problems.

Facts of OrientDB Replication

  • OrientDB uses a third-party API for clustering. The API is called Hazelcast (it’s actually a pretty cool API).
  • So, the clustering configuration is distributed in three different configuration files in OrientDB:
    • one of them is a Hazelcast configuration file (hazelcast.xml),
    •  the other is a distributed server configuration (orientdb-dserver-config.xml)
    •  and even a third file containing some configs in JSON format (default-distributed-db-config.json).
  • OrientDB comes with a default configuration for all these files which theoretically we could use, at least, for testing purposes.
  •  The bin directory of OrientDB contains a (dserver.sh|dserver.bat) script file that can be used to start a distributed server.
  • Before starting the replication service, the databases subject to replication must have been manually copied to all cluster members.

 First Failing Scenario with OrientDB 1.3.0

  •  I connected my office laptop and my personal laptop to a small personal switch in the local area network.
  • After defining a sample database for our product-graph-api I proceeded to…
  • …copying the exact same orientdb-graphed-1.3.0 folder (containing the database) to my two cluster members
  • And then I started the dsever.bat script in both computers.
  •  The console showed both servers recognizing each other and integrating as part of a cluster.
  •  Then I started our product-graph-api REST application and I tried to add an attribute type and…
  • Bang! I got replication exceptions reporting that OrientDB does not support distributed transactions.

 Ok, this one was in part my fault. I did not see that one coming. What went wrong?

Paradoxes of Open Source: #2 – The Non-existing API

  • The Tinkerpop Blueprints API that we are using to access the database uses a database connection called OrientGraph (a TransactionalGraph connection).
  • Evidently, this TransactionalGraph ensures an all-or-nothing policy when committing to the database.
  • However, distributed environments of OrientDB does not support distributed transactions
  • Therefore, it requires that we use a different type of object, one that treats every database operation atomically.
  • Such object is called OrientGraphNoTx, and the creators of the database recommend to use it in the database forums.
  • Paradoxically, this object does not exist in the Tinkerpop Blueprints 2.3.0 API, it only exists in the Blueprints 2.4.0-SNAPSHOT (which evidently is not an official release yet).

So, no wonder why I never used it while working with OrientDB 1.3.0. 

Now, since OrientGraphNoTx does not exist in Blueprints 2.3.0, only in 2.4.0-SNAPSHOT, does that mean there is no way to use Blueprints with OrientDB 1.3.0? I guess it also means there is no way to use replication without using the recommended (and not yet officially released) API like Blueprints 2.4.0-SNAPSHOT.

Paradoxes of Open Source: #3 – Misleading Documentation

  • I had in fact known of the existence of the NonTransactionalGraph object, because it was mentioned in the OrientDB documentation.
  • But the object was not present in my API as I mentioned above.
  • So, there are several releases of the database, but only one version of their documentation.
  •  There is no way to know to which version of the product the documentation applies to.

 In this regard, the documentation should be organized as the Tinkerpop stack does. One set of pages per every supported release.

Second Failing Scenario with OrientDB 1.3.0

Well, since the distributed server does not support distributed transactions, I thought I could change the scenario to do something very simple.

  •  I opened a console in one of the members of the cluster.
  • Connected to my database and issued a simple command
  •  insert into AttributeType set name = ‘Color’
  • And this time it fails with a formatting exception.
  •  As you can see in the link provided in line above, the recommendation of the creators of the database was simply to ‘move on to the next release’ (with its paradoxical implication).

Failing Scenario with OrientDB 1.5.0 Succeeding with OrientDB 1.4.1

Well, since evidently staying in 1.3.0 is not an option I decided to try replication with 1.5.0 which was release yesterday

  • Downloaded the database and installed two exact replicas in my two laptops.
  • Started the dserver.bat in both of them (without changing anything) and I saw them recognizing each other.
  • Opened a console in one of the members and connected to the default database (named tinkerpop).
  • Once there issued the simple command: insert into v set name = ‘Peter’
  •  And guess what? Absolutely nothing was replicated. Nothing happened, no exceptions reported, it simply did not work.

Out frustration I decided to try the exact same scenario with OrientDB 1.4.1 which paradoxically succeed in doing replication. 

So 1.4.1 works but 1.5.0, doesn’t? It was the exact same scenario in the three versions of the database, but it only worked with OrientDB 1.4.1

This somehow tells me the scenario is not flawed.

Conclusions for our case where:

  • Forget about replication on OrientDB 1.3.0 and Blueprints 2.3.0.
  • Forget about using OrientDB 1.4.0, 1.4.1 o 1.5.1 with any Tinkerpop official release 2.3.2. They are bound to the snapshot 2.4.0.
  • OrientDB does not support transactions in a distributed environment.
  • We will probably be forced to move to the latest release, no matter what.
  • The code needs to be changed in such a way that every operation works atomically.
  • If we want replication to work we are forced to use Tinkerpop stack 2.4.0-SNAPSHOT.
  • Which also means we cannot use OrientDB 1.3.0.
  • By using OrientGraphNoTx we compromise ACID transactions on the application.
  • Through OrienGraphNoTX, every change will be atomic and automatically replicated, though.
  • Documentation cannot be trusted, it probably refers to the latest version of the product.
  •  Most likely we will need some form of paid support to ensure we can eventually make this work.
  • If you are using maven you can find the Tinkerpop snapshot release in https://oss.sonatype.org/content/repositories/snapshots/.

Yichuan Wang

unread,
Aug 1, 2013, 3:00:56 AM8/1/13
to orient-...@googlegroups.com
Excellent post! Thank you very much for sharing your detailed results, I was thinking about doing similar things, you have saved me a lot of time.

Luca Garulli

unread,
Aug 1, 2013, 5:06:41 AM8/1/13
to orient-database
Hi Edwin,
you're right with the big mess about API and this is what we're fixing in last release: the Blueprints is the official one. Some old users have migrated, other are happy with previous one. Having only 1 API for graphs simplify our efforts and makes documentation more complete.

The main problem with BP is that it depends on OrientDB, and the OrientDB's GraphDB module depends on Blueprints. So this is a circular dependency problem. Usually we release very very close to the TinkerPop release to leave the API the more "stable" as we can, where the SNAPSHOT is pretty IDENTICAL to the final one (2.4.0-SNAPSHOT now).

OrientDB and Blueprints both changed a lot in the last months so the change is not painless for users, we know, but now users prefer to have more speed and features than a stable API that never changes in time. As soon as BP becomes more "definitive" this pain will be much less. But we're in a transition time where this technology is maturing with the goal to be complete enough for the next years...

So using OrientDB 1.5 with bundled BP 2.4 (of July 30th 2013) is considered stable by OrientDB team and it's the suggested platform to develop apps right now.

About the replication we've test cases that pass so probably there's something in the configuration? Please open a new issue for that attaching the log of both servers.

About distributed transaction this is the most wanted feature right now so we decided to create a "phase1" of such feature for the 1.6 (September 2013): https://github.com/orientechnologies/orientdb/issues/1580.

Lvc@



--
 
---
You received this message because you are subscribed to the Google Groups "OrientDB" group.
To unsubscribe from this group and stop receiving emails from it, send an email to orient-databa...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Edwin Dalorzo

unread,
Aug 1, 2013, 4:49:53 PM8/1/13
to orient-...@googlegroups.com
Lvc@, 

This is really helpful. Thanks for taking the time to respond to my post. I am pretty sure you are a very busy person. I have decided to open an issue about replication because I have struggled with this for several days without any success.

Despite my struggling with a few problems, at the company we still believe OrientDB is a great and very promising product and so far it appears we are going for it. We just hope to make replication happen for us. And we have decide to wait for the phase-1 of the distributed transaction support coming in the next release (hopefully in September).

To everyone following this thread, I did migrated our system to OrientDB 1.5.0. with Blueprints/Gremlin 2.4.0-SNAPTHOT and I ran all my tests and everything seems to work just fine, so far (with the exception of replication).

We are using Gradle. I took snapshot releases of Tinkerpop stack from this repository: https://oss.sonatype.org/content/repositories/snapshots/ 
Interestingly, this snapshots releases no longer work with OrientDB 1.4.1, so I think a migration to 1.5.0 is mandatory.

Two major improvements that I got from upgrading are:

1. I no longer need to access the raw graph database to enable schema support. So I could get rid of the line: graph.getRawGraph().setUseCustomTypes(true);
2. In the Gremlin API I was missing the cast method that would prevent me from having to write awful unchecked warnings. Since the cast method is available in gremlin-java-2.4.0-SNAPSHOT I could beautify a few of my Gremlin queries.


Glenn Goodrich

unread,
Aug 2, 2013, 9:30:52 AM8/2/13
to orient-...@googlegroups.com
A million thanks for this post. It's fantastic.  

We are doing exactly what you did (existing Neo4j, realize that their price is outrageous, look for something else, find OrientDB)  I am in the very early stages of making a Ruby wrapper that mimics the Neo4j gems (which, btw are very mature and feature rich, so it's gonna take awhile to catch up).   Please keep sharing your experiences.  I plan on blogging on OrientDB and the gem (which I've tentatively entitled Oriented) very soon.

Also, we're in the USA, which seems to make us very unique in the OrientDB community....is that true?

Glenn

Edwin Dalorzo

unread,
Aug 2, 2013, 9:55:36 AM8/2/13
to orient-...@googlegroups.com
Glenn, I am very happy to see the community around OrientDB growing. If more of us are working with it, the product will get more and more mature. I have really enjoyed working with OrientDB so far and like you I am planning on blogging soon about the experience. Keep around and we can continue sharing the results of our evaluation of the product. And yes, the prices of Neo4j are ridiculous. 


For all other following the post, we finally figured out what was wrong with the replication. A million thanks to Luca Garulli. Man, Luca, you are the best. I would have not solved this in a million years working on Windows. 

Issues with Path Separator in the ORIENTDB_HOME Environment Variable in Windows

Basically, when you set the ORIENTDB_HOME in Windows, you must not use backslash (\), if you do so, replication will not work. Instead use a forward slash (/). 

And so I was setting my ORIENTBD_HOME=C:\Software\orientdb-graphed-1.5.0 and replication was not working. Following the recomendations of Luca I tested my scenario in a Linux box and it worked, so later I tested again in Windows with forward slash and it all worked like a charm. Simply changed path to ORIENTBD_HOME=C:/Software/orientdb-graphed-1.5.0

So, now you know, avoid pulling your hairs like crazy. All you have to do is to change the path separator. You can see the discussion on the issue here.


Edwin Dalorzo

unread,
Aug 2, 2013, 3:24:04 PM8/2/13
to orient-...@googlegroups.com
The following are some interesting modification that I have to consider upon migrating to OrientDB 1.5.0./Blueprints-2.4.0-SNAPSHOT (from OrientDB 1.3.0/Blueprints-2.30).

I had to review my logic to hit the indices in schema full mode. I noticed some improvements in the API that have simplified my previous work.

  • First, I noticed that in OrientDB 1.5.0 there is not class OGraphVertex which was the base class for all my classes in OrientDB 1.3.0. 
  • Also since the logic in Blueprints-2.3.0 was based on it to choose indices, the logic to hit indices may need to be reviewed.
How Blueprints 2.4.0-SNAPSHOT Hits Indices

The following lines basically a summary of my understanding of the code in com.tinkerpop.blueprints.impls.orient.OrientBaseGraph. If anybody spots something incorret please let me know.

So, if I look for a particular vertex or set of vertices by using key/value arguments (i.e. Graph.getVertices(key,vaue)) the behavior would be as follows:
  • In Schema-free Mode: If I do  g.getVertices("name","Tinkerbell") in this case, it would check if there is an index named V.name and if it is found, it will be used to retrieve the corresponding vertices.
  • In Schema-Full Mode: even if I do g.getVertices("name","Tinkerbell") it would still check would check if there is an index named V.name and if it is found, it will be used to retrieve the corresponding vertices. So, if my class inherits from V I would still hit the index.
  • In Schema-Full Mode if I do g.getVertices("Person.name","Tinkerbell")then it would look for an index named Person.name and it if exist we would hit the index.
Getting All Vertices for a Class:

In the previous version of Blueprints there was not a way to query vertices of particular class, and so I had added field named type to all my classes.  But now we have two ways to achieve this in Blueprints 2.4.0-SNAPSHOT....

  • I can either do: g.getVertices("@class", "Person") where Person is the name of my class
  • Or I can do: g.getVerticesOfClass("Person")
So, for me this meant the following upon migration:
  • If I have  a class named Person, I will need it to make it extend V instead of OGraphVertex.
  • If I want to hit the index with my current code (which does not use the name of the class in getVertices (i.e. g.getVertices("name","Tinkerbell"). I will have to add the indices in the V class
    • Strategy 1 (OrientDB 1.3.0/Blueprints-2.30 or OrientDB-1.5.0/Blueprints-2.4.0-SNAPSHOT)
      • create class Person extends V
      • create property V.name string
      • create index V.name unique
      • This would ensure that if I do g.getVetices("name","Tinkerbell") I hit the index.
      • Disadvantage: but this strategy would pollute the V class with fields and indices for all subclasses.
      • This was indeed my strategy in OrientDB 1.3.0
    • Strategy 2 (OrientDB 1.5.0/Blueprints-2.4.0-SNAPSHOT)
      • create class Person extends V
      • create property Person.name string
      • create index Person.name unique
      • This would ensure that if I do g.getVertices("Person.name","Tinkerbell") I hit the index. 
      • Advantage: this strategy would keep properties and indices int the class they belong to.
Something similar can be said about Edges according to my review of the source code for OrientBaseGraph.

Luca Garulli

unread,
Aug 2, 2013, 4:14:03 PM8/2/13
to orient-database
Hi Edwin,
your post is very very detailed, much better than the official documentation :-)


But feel free to improve it.


Lvc@




--

Edwin Dalorzo

unread,
Aug 2, 2013, 6:15:28 PM8/2/13
to orient-...@googlegroups.com
Thanks, this is very helpful. I will see about contributing to the wiki as soon as I can, @Lvc

Edwin Dalorzo

unread,
Aug 7, 2013, 5:14:39 PM8/7/13
to orient-...@googlegroups.com
This week I have ran into a set of very interesting problems using OrientDB.

Vertex Deletion/Vertex ID Reuse

Every time we delete a vertex, and then insert a new vertex, the IDs of deleted vertices is reused. So OrientDB makes it hard to deal with offline concurrency. Consider the following scenario of offline concurrency:

User A  reads vertex 10:1
User B deletes vertex 10:1
User C inserts a new vertex that ends up becoming the new vertex 10:1
User A update vertex 10:1

Clearly, user A had the intention to update the original 10:1 not the new one. In my case I am using schema-full mode, which at least guarantees that 10:** would always be same class and therefore the overwritten data is at least of the same type, but in schema-less mode, the vertex reusing the same ID could represent an entity totally different from the original (that was deleted). So, this could be rather problematic.

In this post, Luca suggests that to prevent ID reuse we could mark the vertex as "deleted" instead of actually deleting it. But this strategy implies that I will have to change all my queries to ensure no deleted records are returned. I hope there is a way to define a compound automatic index that does not include the "deleted" records.

This strategy also makes it very difficult to write the code in way that is database-agnostic, because this trick is only necessary for OrientDB, and other database may not have this problem. So, now, I am forced to deal with implementation details in repository design.

No Common Transactional Interfaces for OrientGraph/OrientGraphNoTx.

I implemented my code totally decoupled of any particular graph database implementation. Although we are planning on using OrientDB, all the code relies on Blueprints, and I like to keep it in a such a way that it works with both OrientDB and Neo4j or any other supported platforms.

To isolate the details of instantiating a particular graph implementation I have a Graph Connection Factory class which all it does is to return an instance of a TransactionalGraph. My entire application trusts on this interface for all the work I do with the database. When I use OrientDb I instantiate the OrientGraph object, but when I switch to Neo4J I use the Neo4jGraph object, both of them implementations of the Transactional interface.

But, when I switch to the distributed/replicate mode, since it does not support distributed transactions, I had to change my implementation from OrientGraph to OrientGraphNoTx and there I ran into an interesting issue. The class OrientGraphNoTx is not a TransactionalGraph.

It would be simpler if OrientGraphNoTx was indeed a TransactionalGraph object that simply does no-op upon commit or rollback. But now, having a yet second unrelated implementation to deal with my graph forces me to introduce yet another implementation detail to deal with OrientDB.

OrientGraphNoTx Not Retreiving Updates In Distributed Mode.

Finally, while using OrientGraphNoTx in a distributed environment I noticed that it effectively replicates every record that I create, but the data appears as null in every query I run until I restart the database.


Krasimir Cholakov

unread,
Aug 8, 2013, 1:52:48 AM8/8/13
to orient-...@googlegroups.com
Hi Edwin, 

it seems RID is not recycled using plocal.


Andrey Lomakin

unread,
Aug 8, 2013, 2:31:40 AM8/8/13
to orient-database
Hi all,

At first there will be two type of clusters in plocal in first RIDs are still reused and in the second RID is UUIDs (actually they are even more UUIDestet then in Java because have bigger key size - 192 bit) and hash index is used to map rids to correct position which will lead to write degradation of course (but not to read degradation, because we guarantee at most one I/O operation during read). The second type of cluster is going to be used for auto sharding solution.

There were couple discussions about RIDs reuse. It is my personal opinion , according my experience, and if you share your own opinion, you do can influence DB design.

So I see two scenarios why RID reuse can be dangerous.
  1. You remove record, but you have stale record on client side and if you want to updated it you will have unexpected results.
  2. You remove record, but you still have link from other document and if new record will reuse rid link will lead to totally different document.

At first the first scenario was taken in account during plocal implementation if you delete record and reuse rid then new record will have record version which is bigger then deleted one so if you will update this record from client your will have concurrency modification exception.

About 2-nd case in reality we talk about model consistency and the problem is similar to this one http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Orphan_Removal_.28JPA_2.0.29 and from my opinion it should be manged from higher level then db level. So despite that second problem can happen consistency issues usually should be solved on higher level.

It is my personal opinion and I will be really appreciate if you share your own.


 


--
 
---
You received this message because you are subscribed to the Google Groups "OrientDB" group.
To unsubscribe from this group and stop receiving emails from it, send an email to orient-databa...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
With best regards,
Andrey Lomakin


Krasimir Cholakov

unread,
Aug 8, 2013, 3:21:20 AM8/8/13
to orient-...@googlegroups.com
Hi Andrey, 

I asked in the other post but again: is it a big effort to add some database setting which can allow to turn off/on RID recycling?

Thank you.


Andrey Lomakin

unread,
Aug 8, 2013, 3:35:54 AM8/8/13
to orient-database
Hi Krasimir,

Addition of setting is few lines of code, the question is trade off for such setting.
Right now the hash index cluster is under development and if this setting will be switched on the space of deleted records will not be recycled and will be kept forever is it ok for you ?


--
 
---
You received this message because you are subscribed to the Google Groups "OrientDB" group.
To unsubscribe from this group and stop receiving emails from it, send an email to orient-databa...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Andrey Lomakin

unread,
Aug 8, 2013, 3:56:02 AM8/8/13
to orient-database
You can use workaround now,  it gives you similar functionality, you can use automatic hash index and load all records by uuid.

P.S. I can not provide performance test right now, but will later.

Sylvain Spinelli

unread,
Aug 8, 2013, 4:01:46 AM8/8/13
to orient-...@googlegroups.com
Hi Andrey,

In your case 1., I think there is still a problem with RID reuse  :
  1. In client A (or databaseA), you load Record1 that contain a link to Record2
  2. In client B (or databaseB), you delete Record2 and update Record1 for referential integrity. Ok. But, for an other purpose you also create a new record that technically reuse the same RID than Record2.
  3. In client A (or databaseA), you scan your already loaded Record1, discover the link to Record2 and load Record2... Crash ! You get a bad record and mvcc can't help here.

In your case 2., I'm agree that the problem must be treated at higher level. But in many cases, if Orient low layers can ensure that RID are not reused, it offers more possibilities to resolve referential integrity or garbage collector at higher level. For example : asynchronous cleanup reverse links...

In my opinion, tombstone RID should be configurable for each cluster in all implementations (local / plocal). An issue already exist for tombstone RID in local impl : https://github.com/orientechnologies/orientdb/issues/1278


Sylvain

Krasimir Cholakov

unread,
Aug 9, 2013, 5:29:59 AM8/9/13
to orient-...@googlegroups.com
More on the subject sent by mistake as private message:

Hi Krasimir,

Now if we set rid to be not recycled then cluster will keep data forever, but for hash based cluster it is not the case space will be recycled but rids will not, you will be able think about them as about UUIDs.


On Fri, Aug 9, 2013 at 9:44 AM, Krasimir Cholakov wrote: 
Hi Andrey, 

regarding:

Right now the hash index cluster is under development and if this setting will be switched on the space of deleted records will not be recycled and will be kept forever is it ok for you ? 

right now it is not critical to me that the RID is not recycled, but it seems soon I will need that and I do not want to mark the record as deleted but really delete it and also have not-recyclable RID . 
I am not very aware of the hash index cluster concept, but when you say that the deleted records will be kept forever do you mean only now when the cluster is still under development or that will be the normal/expected behavior on the cluster when using not-recyclable RID?

Thank you.

Andrey Lomakin

unread,
Aug 11, 2013, 3:03:31 AM8/11/13
to orient-database
Hi all,
I have added new feature request to track it  https://github.com/orientechnologies/orientdb/issues/1600 .


--
 
---
You received this message because you are subscribed to the Google Groups "OrientDB" group.
To unsubscribe from this group and stop receiving emails from it, send an email to orient-databa...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Luca Garulli

unread,
Aug 11, 2013, 5:04:19 AM8/11/13
to orient-database
I've added another issue related to "local" and "memory" engines: https://github.com/orientechnologies/orientdb/issues/1601

Lvc@

Edwin Dalorzo

unread,
Aug 13, 2013, 11:56:09 AM8/13/13
to orient-...@googlegroups.com
So, based on all answers received so far there is no way to disable RID reuse, not even using plocal I will be able to avoid RID reuse since certain type of clusters may still reuse my RID. (Not clear yet how I can determine the types of my clusters).

I guess the only the only solution I can think of is to define an alternative key for my vertices and define a unique index based on it. And then use this alternative key to look for records.

Edwin Dalorzo

unread,
Aug 13, 2013, 3:04:18 PM8/13/13
to orient-...@googlegroups.com
Ok, today into another interesting scenario, one that I believe I would have never found in the RDBMS.

In summary we are getting OConcurrentModificationException when two independent transactions try to add different edges coming out of the same vertex. I found this kind of disappointing.  To make it clearer let me explain a scenario:

I have two classes AttributeType and AttributeValue. Attribute types are things like color or size, and attribute values would be things like red, green, blue (for color) and small, medium large (for size).

Imagine now that we have two independent transactions trying to create an edge between Color and a value:

Transaction 1 is doing: color---HASVALUE---> Red
Transaction 2 is doing: color---HASVALUE---->Green

Of these two transactions, only the first one to commit would succeed. The second one will get a OConcurrentModificationException since the addition of this edge changed the version of the color vertex.

This makes it impossible for two concurrent transactions to add independent outgoing edges to the same vertex.

Besides retrying the transaction, is there a workaround for this problematic scenario?

Luca Garulli

unread,
Aug 13, 2013, 5:56:04 PM8/13/13
to orient-database
Hi Edwin,
OrientDB has an optimistic approach to remove any server-side locking: when the transaction commits all the versions are checked all together. This require to write concurrent-proof code like:

for( int retry = 0; retry < maxRetries; ++retry ) {
    try{
      // LOOKUP FOR THE INVOICE VERTEX
      Vertex invoice = graph.getVertices("invoiceId", 2323);

      // CREATE A NEW ITEM
      Vertex invoiceItem = graph.addVertex("class:InvoiceItem");
      invoiceItem.field( "price", 1000 );

      // ADD IT TO THE INVOICE
      invoice.addEdge( invoiceItem );

      graph.commit();
      break;
    } catch( OTransactionException e ) {
      // SOMEONE HAVE UPDATE THE INVOICE VERTEX AT THE SAME TIME, RETRY IT
    }
  }
For more information look at:

Lvc@



Edwin Dalorzo

unread,
Aug 13, 2013, 6:39:23 PM8/13/13
to orient-...@googlegroups.com
Thanks Luca!

Yes, I ended up implementing a retry solution. One question, though. I notice there is a ONeedRetryException which is described as an exception that that reports that re-executing the task could succeed. The exception OConcurrentModificationException is a subclass of it.

I does not look like OConcurrentModificationException is a sublass of OTransactionExceptiion. I am bit confused of which exceptions should be interpreted as re-executable.

Any thoughts on this?

Luca Garulli

unread,
Aug 14, 2013, 5:07:35 AM8/14/13
to orient-database
Yes,
when you have transactions you've to catch the OTransactionException, but when you work outside transaction a sub class of ONeedRetryException must be checked.

Probably it would be better having something like a OTransactionalConcurrentModificationException that extends OTransactionException and implements ONeedRetryException?

Lvc@



--

Edwin Dalorzo

unread,
Aug 14, 2013, 9:14:56 AM8/14/13
to orient-...@googlegroups.com
I am confused, can a OConcurrentModificationException occurr outside a transaction?

My changes are occurring inside a transaction, still the exception that I am getting is that of  OConcurrentModificationException and not OTransactionException.

Anyway, having two different hierarchies of exceptions is rather confusing and certainly merging the hierarchy line should simplify things.

I am ended up writing a catch to handle both cases

public <T> T processRequest(Request<T> request) {
   for(int i=1; i <= maxRetries; i++) {
      try {
         return attempt(request);
      } catch(OTransactionException | ONeedRetryException ignore) {
         //ignore so we can retry
         logger.log("Transaction failed: {}. Attempt {}", ignore.getMessage(), i);
      } catch(Exception ex) {
         throw ex; //propagate this to upper layer
      }
   }
   throw new RetryFailedException("Exhausted maximum attempts");
}

I am struggling with the implementation of my retry algorithm. In the wiki documentation about optimistic transactions it would appear that we are suppose to catch Exception and then retry. But I find this too broad. I don't want to retry my transactions in every scenario; for instance, I do not want to retry my transaction if an index fails with a duplicate key.  So, I am confused if a thing like this would be wrapped in a OTransactionException. I guess we need more documentation on the scenarios that trigger this exception and which trigger the ONeedRetryException.

Also some cases of concurrent modifications are not safe to retry. For instance, the scenario I described above with two different clients adding a edge to the same vertex is a scenario in which it should be safe to retry the transaction, but what I have the following scenario:

I have a Employee vertex, two different clients want to update it concurrently. The first one wants to change its name, the second one wants to increase his/her salary. The second one commits first and succeeds and increases the salary of the employee. The first one fails, and then we automatically retry the transaction. The second time it succeeds in updating the employee name, but also overrides the employee salary information with the stale data that it had before the other update.

So, in my opinion the first scenario that I proposed above where two concurrent clients want to add a new edge coming out of the same vertex should never cause a OConcurrentModificationException. For me this is like saying that in a RDBMS two clients attempting to insert a record in same table would cause a concurrent exception because they are trying to use the same foreign key. 

This scenario, in OrientDB, is always going to cause the exception, forcing me to retry every time, which is expensive. I believe the versioning that determines the concurrent exception should be on the edge, not on the vertices being joined.

The second scenario is a valid case for OConcurrentModificationException. In this case, the client should loose the transaction, refresh its data and start all over again. A retry in this case is rather unsafe.

At any rate, if I cannot distinguish between the two possible scenarios of concurrent exceptions, I cannot tell the difference between those that are safe from those that are not, I am forced to either: 1) implementing my own optimistic locking mechanism where I can tell the difference, 2) or let the clients decide when to retry, or 3) risk loosing data when an unsafe retry overwrites a vertex with stale data.


As secondary comment for those trying to implement a database-agnostic API (like I am trying) I found it impossible to write this code using blueprints exclusively. Blueprints does not wrap the underlying database exception in their own database-independent exceptions. Therefore, the writing of this retry mechanism taints my class with database-specific details. This would be one of those things for which we may need different implementations for different databases and ideally this code should be encapsulated, contained in a place where the OrientDb specifics do not propagate outside the boundaries of this class.

Giraldo Rosales

unread,
Jan 27, 2014, 1:27:19 AM1/27/14
to orient-...@googlegroups.com
Has there been an official update to the reuse issue in plocal? Is there a setting to reuse an RID in plocal without storing the deleted records permanently? Also does local store in this way? Reusing RIDs but storing the deleted records permanently? Or does local reuse RIDs and keep clean by removing deleted records?

Thanks!

Andrey Lomakin

unread,
Jan 27, 2014, 5:00:37 AM1/27/14
to orient-database


On Mon, Jan 27, 2014 at 8:27 AM, Giraldo Rosales <nit...@gmail.com> wrote:
Has there been an official update to the reuse issue in plocal? Is there a setting to reuse an RID in plocal without storing the deleted records permanently? Also does local store in this way? Reusing RIDs but storing the deleted records permanently? Or does local reuse RIDs and keep clean by removing deleted records?

Thanks!
--

---
You received this message because you are subscribed to the Google Groups "OrientDB" group.
To unsubscribe from this group and stop receiving emails from it, send an email to orient-databa...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--
Best regards,
Andrey Lomakin.

Orient Technologies
the Company behind OrientDB

Giraldo Rosales

unread,
Jan 27, 2014, 6:45:14 AM1/27/14
to orient-...@googlegroups.com

Yes. I read this. Great  documentation on plocal storage but what about local storage? Does it handle deletions in the same way? I couldn't find  documentation on local deletions.

I also saw a request in this discussion about adding a setting to plocal to reuse RIDs. Was not sure if that request was implemented. I know, as of the latest build, local reuses and plocal doesn't. I was interested in reusing RIDs in plocal.

Thanks!

You received this message because you are subscribed to a topic in the Google Groups "OrientDB" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/orient-database/wXs6HoaiQkY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to orient-databa...@googlegroups.com.

Andrey Lomakin

unread,
Jan 27, 2014, 7:09:11 AM1/27/14
to orient-database
Giraldo,
It is controversial issues, there are doubts that rid reuse will reduce ability to scale database also local storage will be deprecated soon.

What is your problem with RID reuse ?

Giraldo Rosales

unread,
Jan 27, 2014, 7:14:40 AM1/27/14
to orient-...@googlegroups.com

Ah. Ok. Saw your concerns. Will trust them.

I was thinking of something less durable like "votes" or "likes" in a database. Let's say you get a couple thousand votes and then start a new poll. That old, deleted, poll is just wasted RIDs.

Sylvain Spinelli

unread,
Jan 27, 2014, 8:24:33 AM1/27/14
to orient-...@googlegroups.com
For a customer, I have a use case where more than 50000 records are deleted and re-created every night by batch. Not reuse RID can be a problem here.

Ideally I think we should have a customizable interface "RidFactory" with 3 default implementations : "never reuse", "reuse immediately", "reuse later" (means reuse after a reasonable delay, 1 day for example).

My 2 cents contribution ;)


Sylvain

Giraldo Rosales

unread,
Jan 27, 2014, 8:54:35 AM1/27/14
to orient-...@googlegroups.com

That would be a great solution. Or even if it's an eventual reuse. Where if and when the RIDs run out (I am aware the limit is far beyond trillions), the database will start from 0 again and reuse any unused RIDs.

Andrey,
The fact that local will be deprecated helps in choosing the right option between the two. Thanks.

Andrey Lomakin

unread,
Jan 27, 2014, 9:06:10 AM1/27/14
to orient-database
So as I can see it the problem is not rid reuse but space reuse, does not it ?

Andrey Lomakin

unread,
Jan 27, 2014, 9:14:41 AM1/27/14
to orient-database
Sylvain, 
About your issue,

So if you create and delete 50 000 records every night, it means that every night you lose 50 * 12 = 600 kB of disk space , so for the 2 years it will be 600 * 365 = 420 Mb

I took 2 years because I think we come up with space reuse solution till this time :-).

Is it noticeable for your application ?

Sylvain Spinelli

unread,
Jan 27, 2014, 9:23:40 AM1/27/14
to orient-...@googlegroups.com

Le 27/01/2014 15:06, Andrey Lomakin a écrit :
> So as I can see it the problem is not rid reuse but space reuse, does
> not it ?
>
You're right, for my use case, the problem is space reuse.

Giraldo Rosales

unread,
Jan 27, 2014, 9:23:53 AM1/27/14
to orient-...@googlegroups.com

Correct. Just wanted peace of mind if we keep the database clean on the app side, we could have unlimited RIDs.

So if hundreds of thousands of RIDs are deleted on a regular basis, those RIDs are not gone for good. They would be reused sometime in the future.

The longer the period between reuse, the better. This would also relieve the concerns of those who may not want the confusion of immediate reuse.

Sylvain Spinelli

unread,
Jan 27, 2014, 9:31:59 AM1/27/14
to orient-...@googlegroups.com
No problem, It was just a real use case for the discussion. We can deal with this small issue.

Thanks,
Sylvain
Reply all
Reply to author
Forward
0 new messages