Concurrency

OrientDB uses an optimistic approach to concurrency. Optimistic Concurrency Control, or OCC assumes that multiple transactions can compete frequently without interfering with each other.

Optimistic Concurrency in OrientDB

Optimistic concurrency control is used in environments with low data contention. That is, where conflicts are rare and transactions can complete without the expense of managing locks and without having transactions wait for locks to clear. This means a reduced throughput over other concurrency control methods.

OrientDB uses OCC for both Atomic Operations and Transactions.

Atomic Operations

OrientDB supports Multi-Version Concurrency Control, or MVCC, with atomic operations. This allows it to avoid locking server side resources. At the same time, it checks the version in the database. If the version is equal to the record version contained in the operation, the operation is successful. If the version found is higher than the record version contained in the operation, then another thread or user has already updated the same record. In this case, OrientDB generates an OConcurrentModificationException exception.

Given that behavior of this kind is normal on systems that use optimistic concurrency control, developers need to write concurrency-proof code. Under this design, the application retries transactions x times before reporting the error. It does this by catching the exception, reloading the affected records and attempting to update them again. For example, consider the code for saving a document,

  1. int maxRetries = 10;
  2. List<ODocument> result = db.query("SELECT FROM Client WHERE id = '39w39D32d2d'");
  3. ODocument address = result.get(0);
  4. for (int retry = 0; retry < maxRetries; ++retry) {
  5. try {
  6. // LOOKUP FOR THE INVOICE VERTEX
  7. address.field( "street", street );
  8. address.field( "zip", zip );
  9. address.field( "city", cityName );
  10. address.field( "country", countryName );
  11. address.save();
  12. // EXIT FROM RETRY LOOP
  13. break;
  14. }
  15. catch( ONeedRetryException e ) {
  16. // IF SOMEONE UPDATES THE ADDRESS DOCUMENT
  17. // AT THE SAME TIME, RETRY IT.
  18. }
  19. }

Transactions

OrientDB supports optimistic transactions. The database does not use locks when transactions are running, but when the transaction commits, each record (document or graph element) version is checked to see if there have been updates from another client. For this reason, you need to code your applications to be concurrency-proof.

Optimistic concurrency requires that you retire the transaction in the event of conflicts. For example, consider a case where you want to connect a new vertex to an existing vertex:

  1. int maxRetries = 10;
  2. for (int retry = 0; retry < maxRetries; ++retry) {
  3. try {
  4. // LOOKUP FOR THE INVOICE VERTEX
  5. Vertex invoice = graph.getVertices("invoiceId", 2323);
  6. // CREATE A NEW ITEM
  7. Vertex invoiceItem = graph.addVertex("class:InvoiceItem");
  8. invoiceItem.field("price", 1000);
  9. // ADD IT TO THE INVOICE
  10. invoice.addEdge(invoiceItem);
  11. graph.commit();
  12. // EXIT FROM RETRY LOOP
  13. break;
  14. }
  15. catch( OConcurrentModificationException e ) {
  16. // SOMEONE HAS UPDATED THE INVOICE VERTEX
  17. // AT THE SAME TIME, RETRY IT
  18. }
  19. }

Concurrency Level

In order to guarantee atomicity and consistency, OrientDB uses an exclusive lock on the storage during transaction commits. This means that transactions are serialized.

Given this limitation, developers with OrientDB are working on improving parallelism to achieve better scalability on multi-core machines, by optimizing internal structure to avoid exclusive locking.

Concurrency when Adding Edges

Consider the case where multiple clients attempt to add edges on the same vertex. OrientDB could throw the OConcurrentModificationException exception. This occurs because collections of edges are kept on vertices, meaning that, every time OrientDB adds or removes an edge, both vertices update and their versions increment. You can avoid this issue by using RIDBAG Bonsai structure, which are never embedded, so the edge never updates the vertices.

To use this configuration at run-time, before launching OrientDB, use this code:

  1. OGlobalConfiguration.RID_BAG_EMBEDDED_TO_SBTREEBONSAI_THRESHOLD.setValue(-1);

Alternatively, you can set a parameter for the Java virtual-machine on startup, or even at run-time, before OrientDB is used:

  1. $ java -DridBag.embeddedToSbtreeBonsaiThreshold=-1
NOTE While running in distributed mode SBTrees are not supported. If using a distributed database then you must set
  1. ridBag.embeddedToSbtreeBonsaiThreshold = Integer.MAX_VALUE
to avoid replication errors.

Troubleshooting

Reduce Transaction Size

On occasion, OrientDB throws the OConcurrentModificationException exception even when you concurrently update the first element. In particularly large transactions, where you have thousands of records involved in a transaction, one changed record is enough to roll the entire process back with an OConcurrentModificationException exception.

To avoid issues of this kind, if you plan to update many elements in the same transaction with high-concurrency on the same vertices, a best practice is to reduce the transaction size.