From 70f4362bdf63c6de6f408029bdcb6951f02867c6 Mon Sep 17 00:00:00 2001
From: Andrey Lomakin <lomakin.andrey@gmail.com>
Date: Mon, 31 Oct 2016 22:28:34 +0200
Subject: [PATCH] [orientdb] OrientDB was updated to 2.2.10 and multithreading
 bugs fixed (#848)

---
 orientdb/README.md                            |   6 -
 .../com/yahoo/ycsb/db/OrientDBClient.java     | 328 +++++++++---------
 .../com/yahoo/ycsb/db/OrientDBClientTest.java | 110 +++---
 pom.xml                                       |   2 +-
 4 files changed, 231 insertions(+), 215 deletions(-)

diff --git a/orientdb/README.md b/orientdb/README.md
index 578a5abb..ed637282 100644
--- a/orientdb/README.md
+++ b/orientdb/README.md
@@ -60,12 +60,6 @@ WARNING: Creating a new database will be done safely with multiple threads on a
 * ```orientdb.newdb``` - Overwrite the database if it already exists.
     * Only effects the ```load``` phase.
     * Default: ```false```
-* ```orientdb.intent``` - Declare an Intent to the database.
-    * This is an optimization feature provided by OrientDB: http://orientdb.com/docs/2.1/Performance-Tuning.html#massive-insertion
-    * Possible values are:
-        * massiveinsert
-        * massiveread
-        * nocache
 * ```orientdb.remote.storagetype``` - Storage type of the database on remote server
     * This is only required if using a ```remote:``` connection url
 
diff --git a/orientdb/src/main/java/com/yahoo/ycsb/db/OrientDBClient.java b/orientdb/src/main/java/com/yahoo/ycsb/db/OrientDBClient.java
index d1d29205..5c54d0c8 100644
--- a/orientdb/src/main/java/com/yahoo/ycsb/db/OrientDBClient.java
+++ b/orientdb/src/main/java/com/yahoo/ycsb/db/OrientDBClient.java
@@ -17,197 +17,191 @@
 
 package com.yahoo.ycsb.db;
 
-import com.orientechnologies.common.exception.OException;
-import com.orientechnologies.orient.client.remote.OEngineRemote;
 import com.orientechnologies.orient.client.remote.OServerAdmin;
-import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
+import com.orientechnologies.orient.core.config.OGlobalConfiguration;
+import com.orientechnologies.orient.core.db.OPartitionedDatabasePool;
 import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
-import com.orientechnologies.orient.core.db.record.OIdentifiable;
 import com.orientechnologies.orient.core.dictionary.ODictionary;
-import com.orientechnologies.orient.core.exception.ODatabaseException;
+import com.orientechnologies.orient.core.exception.OConcurrentModificationException;
 import com.orientechnologies.orient.core.index.OIndexCursor;
-import com.orientechnologies.orient.core.intent.OIntentMassiveInsert;
-import com.orientechnologies.orient.core.intent.OIntentMassiveRead;
-import com.orientechnologies.orient.core.intent.OIntentNoCache;
 import com.orientechnologies.orient.core.record.ORecord;
 import com.orientechnologies.orient.core.record.impl.ODocument;
-import com.yahoo.ycsb.ByteIterator;
-import com.yahoo.ycsb.DB;
-import com.yahoo.ycsb.DBException;
-import com.yahoo.ycsb.Status;
-import com.yahoo.ycsb.StringByteIterator;
+import com.yahoo.ycsb.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.Set;
-import java.util.Vector;
+import java.io.File;
+import java.util.*;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * OrientDB client for YCSB framework.
  */
 public class OrientDBClient extends DB {
+  private static final String URL_PROPERTY         = "orientdb.url";
+  private static final String URL_PROPERTY_DEFAULT =
+      "plocal:." + File.separator + "target" + File.separator + "databases" + File.separator + "ycsb";
 
-  private static final String CLASS = "usertable";
-  private ODatabaseDocumentTx db;
-  private ODictionary<ORecord> dictionary;
-  private boolean isRemote = false;
-
-  private static final String URL_PROPERTY = "orientdb.url";
-
-  private static final String USER_PROPERTY = "orientdb.user";
+  private static final String USER_PROPERTY         = "orientdb.user";
   private static final String USER_PROPERTY_DEFAULT = "admin";
 
-  private static final String PASSWORD_PROPERTY = "orientdb.password";
+  private static final String PASSWORD_PROPERTY         = "orientdb.password";
   private static final String PASSWORD_PROPERTY_DEFAULT = "admin";
 
-  private static final String NEWDB_PROPERTY = "orientdb.newdb";
+  private static final String NEWDB_PROPERTY         = "orientdb.newdb";
   private static final String NEWDB_PROPERTY_DEFAULT = "false";
 
   private static final String STORAGE_TYPE_PROPERTY = "orientdb.remote.storagetype";
 
-  private static final String INTENT_PROPERTY = "orientdb.intent";
-  private static final String INTENT_PROPERTY_DEFAULT = "";
+  private static final String ORIENTDB_DOCUMENT_TYPE = "document";
 
-  private static final String DO_TRANSACTIONS_PROPERTY = "dotransactions";
-  private static final String DO_TRANSACTIONS_PROPERTY_DEFAULT = "true";
+  private static final String CLASS = "usertable";
 
-  private static final String ORIENTDB_DOCUMENT_TYPE = "document";
-  private static final String ORIENTDB_MASSIVEINSERT = "massiveinsert";
-  private static final String ORIENTDB_MASSIVEREAD = "massiveread";
-  private static final String ORIENTDB_NOCACHE = "nocache";
+  private static final Lock    INIT_LOCK = new ReentrantLock();
+  private static       boolean dbChecked = false;
+  private static volatile OPartitionedDatabasePool databasePool;
+  private static boolean initialized   = false;
+  private static int     clientCounter = 0;
+
+  private boolean isRemote = false;
 
   private static final Logger LOG = LoggerFactory.getLogger(OrientDBClient.class);
 
   /**
-   * This method abstracts the administration of OrientDB namely creating and connecting to a database.
-   * Creating a database needs to be done in a synchronized method so that multiple threads do not all try
-   * to run the creation operation simultaneously, this ends in failure.
-   *
-   * @param props Workload properties object
-   * @return a usable ODatabaseDocumentTx object
-   * @throws DBException
+   * Initialize any state for this DB. Called once per DB instance; there is one DB instance per client thread.
    */
-  private static synchronized ODatabaseDocumentTx initDB(Properties props) throws DBException {
-    String url = props.getProperty(URL_PROPERTY);
+  public void init() throws DBException {
+    // initialize OrientDB driver
+    final Properties props = getProperties();
+    String url = props.getProperty(URL_PROPERTY, URL_PROPERTY_DEFAULT);
     String user = props.getProperty(USER_PROPERTY, USER_PROPERTY_DEFAULT);
+
     String password = props.getProperty(PASSWORD_PROPERTY, PASSWORD_PROPERTY_DEFAULT);
     Boolean newdb = Boolean.parseBoolean(props.getProperty(NEWDB_PROPERTY, NEWDB_PROPERTY_DEFAULT));
     String remoteStorageType = props.getProperty(STORAGE_TYPE_PROPERTY);
-    Boolean isrun = Boolean.parseBoolean(props.getProperty(DO_TRANSACTIONS_PROPERTY, DO_TRANSACTIONS_PROPERTY_DEFAULT));
-
-    ODatabaseDocumentTx dbconn;
-
-    if (url == null) {
-      throw new DBException(String.format("Required property \"%s\" missing for OrientDBClient", URL_PROPERTY));
-    }
 
-    LOG.info("OrientDB loading database url = " + url);
+    INIT_LOCK.lock();
+    try {
+      clientCounter++;
+      if (!initialized) {
+        OGlobalConfiguration.dumpConfiguration(System.out);
 
-    // If using a remote database, use the OServerAdmin interface to connect
-    if (url.startsWith(OEngineRemote.NAME)) {
-      if (remoteStorageType == null) {
-        throw new DBException("When connecting to a remote OrientDB instance, " +
-          "specify a database storage type (plocal or memory) with " + STORAGE_TYPE_PROPERTY);
-      }
+        LOG.info("OrientDB loading database url = " + url);
 
-      try {
-        OServerAdmin server = new OServerAdmin(url).connect(user, password);
+        ODatabaseDocumentTx db = new ODatabaseDocumentTx(url);
 
-        if (server.existsDatabase()) {
-          if (newdb && !isrun) {
-            LOG.info("OrientDB dropping and recreating fresh db on remote server.");
-            server.dropDatabase(remoteStorageType);
-            server.createDatabase(server.getURL(), ORIENTDB_DOCUMENT_TYPE, remoteStorageType);
-          }
-        } else {
-          LOG.info("OrientDB database not found, creating fresh db");
-          server.createDatabase(server.getURL(), ORIENTDB_DOCUMENT_TYPE, remoteStorageType);
+        if (db.getStorage().isRemote()) {
+          isRemote = true;
         }
 
-        server.close();
-        dbconn = new ODatabaseDocumentTx(url).open(user, password);
-      } catch (IOException | OException e) {
-        throw new DBException(String.format("Error interfacing with %s", url), e);
-      }
-    } else {
-      try {
-        dbconn = new ODatabaseDocumentTx(url);
-        if (dbconn.exists()) {
-          dbconn.open(user, password);
-          if (newdb && !isrun) {
-            LOG.info("OrientDB dropping and recreating fresh db.");
-            dbconn.drop();
-            dbconn.create();
+        if (!dbChecked) {
+          if (!isRemote) {
+            if (newdb) {
+              if (db.exists()) {
+                db.open(user, password);
+                LOG.info("OrientDB drop and recreate fresh db");
+
+                db.drop();
+              }
+
+              db.create();
+            } else {
+              if (!db.exists()) {
+                LOG.info("OrientDB database not found, creating fresh db");
+
+                db.create();
+              }
+            }
+          } else {
+            OServerAdmin server = new OServerAdmin(url).connect(user, password);
+
+            if (remoteStorageType == null) {
+              throw new DBException(
+                  "When connecting to a remote OrientDB instance, "
+                      + "specify a database storage type (plocal or memory) with "
+                      + STORAGE_TYPE_PROPERTY);
+            }
+
+            if (newdb) {
+              if (server.existsDatabase()) {
+                LOG.info("OrientDB drop and recreate fresh db");
+
+                server.dropDatabase(remoteStorageType);
+              }
+
+              server.createDatabase(db.getName(), ORIENTDB_DOCUMENT_TYPE, remoteStorageType);
+            } else {
+              if (!server.existsDatabase()) {
+
+                LOG.info("OrientDB database not found, creating fresh db");
+                server.createDatabase(server.getURL(), ORIENTDB_DOCUMENT_TYPE, remoteStorageType);
+              }
+            }
+
+            server.close();
           }
-        } else {
-          LOG.info("OrientDB database not found, creating fresh db");
-          dbconn.create();
-        }
-      } catch (ODatabaseException e) {
-        throw new DBException(String.format("Error interfacing with %s", url), e);
-      }
-    }
 
-    if (dbconn == null) {
-      throw new DBException("Could not establish connection to: " + url);
-    }
+          dbChecked = true;
+        }
 
-    LOG.info("OrientDB connection created with " + url);
-    return dbconn;
-  }
+        if (db.isClosed()) {
+          db.open(user, password);
+        }
 
-  @Override
-  public void init() throws DBException {
-    Properties props = getProperties();
+        if (!db.getMetadata().getSchema().existsClass(CLASS)) {
+          db.getMetadata().getSchema().createClass(CLASS);
+        }
 
-    String intent = props.getProperty(INTENT_PROPERTY, INTENT_PROPERTY_DEFAULT);
+        db.close();
 
-    db = initDB(props);
+        if (databasePool == null) {
+          databasePool = new OPartitionedDatabasePool(url, user, password);
+        }
 
-    if (db.getURL().startsWith(OEngineRemote.NAME)) {
-      isRemote = true;
+        initialized = true;
+      }
+    } catch (Exception e) {
+      LOG.error("Could not initialize OrientDB connection pool for Loader: " + e.toString());
+      e.printStackTrace();
+    } finally {
+      INIT_LOCK.unlock();
     }
 
-    dictionary = db.getMetadata().getIndexManager().getDictionary();
-    if (!db.getMetadata().getSchema().existsClass(CLASS)) {
-      db.getMetadata().getSchema().createClass(CLASS);
-    }
+  }
 
-    if (intent.equals(ORIENTDB_MASSIVEINSERT)) {
-      LOG.info("Declaring intent of MassiveInsert.");
-      db.declareIntent(new OIntentMassiveInsert());
-    } else if (intent.equals(ORIENTDB_MASSIVEREAD)) {
-      LOG.info("Declaring intent of MassiveRead.");
-      db.declareIntent(new OIntentMassiveRead());
-    } else if (intent.equals(ORIENTDB_NOCACHE)) {
-      LOG.info("Declaring intent of NoCache.");
-      db.declareIntent(new OIntentNoCache());
-    }
+  OPartitionedDatabasePool getDatabasePool() {
+    return databasePool;
   }
 
   @Override
   public void cleanup() throws DBException {
-    // Set this thread's db reference (needed for thread safety in testing)
-    ODatabaseRecordThreadLocal.INSTANCE.set(db);
+    INIT_LOCK.lock();
+    try {
+      clientCounter--;
+      if (clientCounter == 0) {
+        databasePool.close();
+      }
 
-    if (db != null) {
-      db.close();
-      db = null;
+      databasePool = null;
+      initialized = false;
+    } finally {
+      INIT_LOCK.unlock();
     }
+
   }
 
   @Override
   public Status insert(String table, String key, HashMap<String, ByteIterator> values) {
-    try {
+    try (ODatabaseDocumentTx db = databasePool.acquire()) {
       final ODocument document = new ODocument(CLASS);
-      for (Entry<String, String> entry : StringByteIterator.getStringMap(values).entrySet()) {
+
+      for (Map.Entry<String, String> entry : StringByteIterator.getStringMap(values).entrySet()) {
         document.field(entry.getKey(), entry.getValue());
       }
+
       document.save();
+      final ODictionary<ORecord> dictionary = db.getMetadata().getIndexManager().getDictionary();
       dictionary.put(key, document);
 
       return Status.OK;
@@ -219,18 +213,24 @@ public class OrientDBClient extends DB {
 
   @Override
   public Status delete(String table, String key) {
-    try {
-      dictionary.remove(key);
-      return Status.OK;
-    } catch (Exception e) {
-      e.printStackTrace();
+    while (true) {
+      try (ODatabaseDocumentTx db = databasePool.acquire()) {
+        final ODictionary<ORecord> dictionary = db.getMetadata().getIndexManager().getDictionary();
+        dictionary.remove(key);
+        return Status.OK;
+      } catch (OConcurrentModificationException cme) {
+        continue;
+      } catch (Exception e) {
+        e.printStackTrace();
+        return Status.ERROR;
+      }
     }
-    return Status.ERROR;
   }
 
   @Override
   public Status read(String table, String key, Set<String> fields, HashMap<String, ByteIterator> result) {
-    try {
+    try (ODatabaseDocumentTx db = databasePool.acquire()) {
+      final ODictionary<ORecord> dictionary = db.getMetadata().getIndexManager().getDictionary();
       final ODocument document = dictionary.get(key);
       if (document != null) {
         if (fields != null) {
@@ -252,48 +252,63 @@ public class OrientDBClient extends DB {
 
   @Override
   public Status update(String table, String key, HashMap<String, ByteIterator> values) {
-    try {
-      final ODocument document = dictionary.get(key);
-      if (document != null) {
-        for (Entry<String, String> entry : StringByteIterator.getStringMap(values).entrySet()) {
-          document.field(entry.getKey(), entry.getValue());
+    while (true) {
+      try (ODatabaseDocumentTx db = databasePool.acquire()) {
+        final ODictionary<ORecord> dictionary = db.getMetadata().getIndexManager().getDictionary();
+        final ODocument document = dictionary.get(key);
+        if (document != null) {
+          for (Map.Entry<String, String> entry : StringByteIterator.getStringMap(values).entrySet()) {
+            document.field(entry.getKey(), entry.getValue());
+          }
+
+          document.save();
+          return Status.OK;
         }
-        document.save();
-        return Status.OK;
+      } catch (OConcurrentModificationException cme) {
+        continue;
+      } catch (Exception e) {
+        e.printStackTrace();
+        return Status.ERROR;
       }
-    } catch (Exception e) {
-      e.printStackTrace();
     }
-    return Status.ERROR;
   }
 
   @Override
   public Status scan(String table, String startkey, int recordcount, Set<String> fields,
-                     Vector<HashMap<String, ByteIterator>> result) {
+      Vector<HashMap<String, ByteIterator>> result) {
+
     if (isRemote) {
       // Iterator methods needed for scanning are Unsupported for remote database connections.
       LOG.warn("OrientDB scan operation is not implemented for remote database connections.");
       return Status.NOT_IMPLEMENTED;
     }
 
-    try {
-      int entrycount = 0;
+    try (ODatabaseDocumentTx db = databasePool.acquire()) {
+      final ODictionary<ORecord> dictionary = db.getMetadata().getIndexManager().getDictionary();
       final OIndexCursor entries = dictionary.getIndex().iterateEntriesMajor(startkey, true, true);
 
-      while (entries.hasNext() && entrycount < recordcount) {
-        final OIdentifiable entry = entries.next();
-        final ODocument document = entry.getRecord();
+      int currentCount = 0;
+      while (entries.hasNext()) {
+        final ODocument document = entries.next().getRecord();
 
-        final HashMap<String, ByteIterator> map = new HashMap<String, ByteIterator>();
+        final HashMap<String, ByteIterator> map = new HashMap<>();
         result.add(map);
 
-        if (fields != null && !fields.isEmpty()) {
+        if (fields != null) {
           for (String field : fields) {
             map.put(field, new StringByteIterator((String) document.field(field)));
           }
+        } else {
+          for (String field : document.fieldNames()) {
+            map.put(field, new StringByteIterator((String) document.field(field)));
+          }
         }
 
-        entrycount++;
+        currentCount++;
+
+        if (currentCount >= recordcount) {
+          break;
+        }
       }
 
       return Status.OK;
@@ -302,11 +317,4 @@ public class OrientDBClient extends DB {
     }
     return Status.ERROR;
   }
-
-  /**
-   * Access method to db variable for unit testing.
-   **/
-  ODatabaseDocumentTx getDB() {
-    return db;
-  }
 }
diff --git a/orientdb/src/test/java/com/yahoo/ycsb/db/OrientDBClientTest.java b/orientdb/src/test/java/com/yahoo/ycsb/db/OrientDBClientTest.java
index 140a9773..d72607cc 100644
--- a/orientdb/src/test/java/com/yahoo/ycsb/db/OrientDBClientTest.java
+++ b/orientdb/src/test/java/com/yahoo/ycsb/db/OrientDBClientTest.java
@@ -17,6 +17,8 @@
 
 package com.yahoo.ycsb.db;
 
+import com.orientechnologies.orient.core.db.OPartitionedDatabasePool;
+import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
 import com.orientechnologies.orient.core.dictionary.ODictionary;
 import com.orientechnologies.orient.core.record.ORecord;
 import com.orientechnologies.orient.core.record.impl.ODocument;
@@ -25,6 +27,7 @@ import com.yahoo.ycsb.DBException;
 import com.yahoo.ycsb.StringByteIterator;
 
 import org.junit.*;
+
 import java.util.*;
 
 import static org.junit.Assert.*;
@@ -34,14 +37,13 @@ import static org.junit.Assert.*;
  */
 public class OrientDBClientTest {
   // TODO: This must be copied because it is private in OrientDBClient, but this should defer to table property.
-  private static final String CLASS = "usertable";
-  private static final int FIELD_LENGTH = 32;
+  private static final String CLASS        = "usertable";
+  private static final int    FIELD_LENGTH = 32;
   private static final String FIELD_PREFIX = "FIELD";
-  private static final String KEY_PREFIX = "user";
-  private static final int NUM_FIELDS = 3;
-  private static final String TEST_DB_URL = "memory:test";
+  private static final String KEY_PREFIX   = "user";
+  private static final int    NUM_FIELDS   = 3;
+  private static final String TEST_DB_URL  = "memory:test";
 
-  private static ODictionary<ORecord> orientDBDictionary;
   private static OrientDBClient orientDBClient = null;
 
   @Before
@@ -54,7 +56,6 @@ public class OrientDBClientTest {
 
     orientDBClient.setProperties(p);
     orientDBClient.init();
-    orientDBDictionary = orientDBClient.getDB().getDictionary();
   }
 
   @After
@@ -101,12 +102,17 @@ public class OrientDBClientTest {
     String insertKey = "user0";
     Map<String, ByteIterator> insertMap = insertRow(insertKey);
 
-    ODocument result = orientDBDictionary.get(insertKey);
+    OPartitionedDatabasePool pool = orientDBClient.getDatabasePool();
+    try(ODatabaseDocumentTx db = pool.acquire()) {
+      ODictionary<ORecord> dictionary = db.getDictionary();
+      ODocument result = dictionary.get(insertKey);
 
-    assertTrue("Assert a row was inserted.", result != null);
+      assertTrue("Assert a row was inserted.", result != null);
 
-    for (int i = 0; i < NUM_FIELDS; i++) {
-      assertEquals("Assert all inserted columns have correct values.", result.field(FIELD_PREFIX + i), insertMap.get(FIELD_PREFIX + i).toString());
+      for (int i = 0; i < NUM_FIELDS; i++) {
+        assertEquals("Assert all inserted columns have correct values.", result.field(FIELD_PREFIX + i),
+            insertMap.get(FIELD_PREFIX + i).toString());
+      }
     }
   }
 
@@ -117,14 +123,19 @@ public class OrientDBClientTest {
     String user1 = "user1";
     String user2 = "user2";
 
-    // Manually insert three documents
-    for(String key: Arrays.asList(user0, user1, user2)) {
-      ODocument doc = new ODocument(CLASS);
-      for (int i = 0; i < NUM_FIELDS; i++) {
-        doc.field(FIELD_PREFIX + i, preupdateString);
+    OPartitionedDatabasePool pool = orientDBClient.getDatabasePool();
+    try(ODatabaseDocumentTx db = pool.acquire()) {
+      // Manually insert three documents
+      for (String key : Arrays.asList(user0, user1, user2)) {
+        ODocument doc = new ODocument(CLASS);
+        for (int i = 0; i < NUM_FIELDS; i++) {
+          doc.field(FIELD_PREFIX + i, preupdateString);
+        }
+        doc.save();
+
+        ODictionary<ORecord> dictionary = db.getDictionary();
+        dictionary.put(key, doc);
       }
-      doc.save();
-      orientDBDictionary.put(key, doc);
     }
 
     HashMap<String, ByteIterator> updateMap = new HashMap<>();
@@ -134,22 +145,26 @@ public class OrientDBClientTest {
 
     orientDBClient.update(CLASS, user1, updateMap);
 
-    // Ensure that user0 record was not changed
-    ODocument result = orientDBDictionary.get(user0);
-    for (int i = 0; i < NUM_FIELDS; i++) {
-      assertEquals("Assert first row fields contain preupdateString", result.field(FIELD_PREFIX + i), preupdateString);
-    }
+    try(ODatabaseDocumentTx db = pool.acquire()) {
+      ODictionary<ORecord> dictionary = db.getDictionary();
+      // Ensure that user0 record was not changed
+      ODocument result = dictionary.get(user0);
+      for (int i = 0; i < NUM_FIELDS; i++) {
+        assertEquals("Assert first row fields contain preupdateString", result.field(FIELD_PREFIX + i), preupdateString);
+      }
 
-    // Check that all the columns have expected values for user1 record
-    result = orientDBDictionary.get(user1);
-    for (int i = 0; i < NUM_FIELDS; i++) {
-      assertEquals("Assert updated row fields are correct", result.field(FIELD_PREFIX + i), updateMap.get(FIELD_PREFIX + i).toString());
-    }
+      // Check that all the columns have expected values for user1 record
+      result = dictionary.get(user1);
+      for (int i = 0; i < NUM_FIELDS; i++) {
+        assertEquals("Assert updated row fields are correct", result.field(FIELD_PREFIX + i),
+            updateMap.get(FIELD_PREFIX + i).toString());
+      }
 
-    // Ensure that user2 record was not changed
-    result = orientDBDictionary.get(user2);
-    for (int i = 0; i < NUM_FIELDS; i++) {
-      assertEquals("Assert third row fields contain preupdateString", result.field(FIELD_PREFIX + i), preupdateString);
+      // Ensure that user2 record was not changed
+      result = dictionary.get(user2);
+      for (int i = 0; i < NUM_FIELDS; i++) {
+        assertEquals("Assert third row fields contain preupdateString", result.field(FIELD_PREFIX + i), preupdateString);
+      }
     }
   }
 
@@ -164,7 +179,7 @@ public class OrientDBClientTest {
     readFields.add("FIELD0");
     orientDBClient.read(CLASS, insertKey, readFields, readResultMap);
     assertEquals("Assert that result has correct number of fields", readFields.size(), readResultMap.size());
-    for (String field: readFields) {
+    for (String field : readFields) {
       assertEquals("Assert " + field + " was read correctly", insertMap.get(field).toString(), readResultMap.get(field).toString());
     }
 
@@ -175,7 +190,7 @@ public class OrientDBClientTest {
     readFields.add("FIELD2");
     orientDBClient.read(CLASS, insertKey, readFields, readResultMap);
     assertEquals("Assert that result has correct number of fields", readFields.size(), readResultMap.size());
-    for (String field: readFields) {
+    for (String field : readFields) {
       assertEquals("Assert " + field + " was read correctly", insertMap.get(field).toString(), readResultMap.get(field).toString());
     }
   }
@@ -192,9 +207,14 @@ public class OrientDBClientTest {
 
     orientDBClient.delete(CLASS, user1);
 
-    assertNotNull("Assert user0 still exists", orientDBDictionary.get(user0));
-    assertNull("Assert user1 does not exist", orientDBDictionary.get(user1));
-    assertNotNull("Assert user2 still exists", orientDBDictionary.get(user2));
+    OPartitionedDatabasePool pool = orientDBClient.getDatabasePool();
+    try(ODatabaseDocumentTx db = pool.acquire()) {
+      ODictionary<ORecord> dictionary = db.getDictionary();
+
+      assertNotNull("Assert user0 still exists", dictionary.get(user0));
+      assertNull("Assert user1 does not exist", dictionary.get(user1));
+      assertNotNull("Assert user2 still exists", dictionary.get(user2));
+    }
   }
 
   @Test
@@ -208,7 +228,7 @@ public class OrientDBClientTest {
     Set<String> fieldSet = new HashSet<>();
     fieldSet.add("FIELD0");
     fieldSet.add("FIELD1");
-    int startIndex = 1;
+    int startIndex = 0;
     int resultRows = 3;
 
     Vector<HashMap<String, ByteIterator>> resultVector = new Vector<>();
@@ -217,20 +237,14 @@ public class OrientDBClientTest {
     // Check the resultVector is the correct size
     assertEquals("Assert the correct number of results rows were returned", resultRows, resultVector.size());
 
-    /**
-     * Part of the known issue about the broken iterator in orientdb is that the iterator
-     * starts at index 1 instead of index 0. Because of this, to test it we must increment
-     * the start index. When that known issue has been fixed, remove the increment below.
-     * Track the issue here: https://github.com/orientechnologies/orientdb/issues/5541
-     * This fix was implemented for orientechnologies:orientdb-client:2.1.8
-     */
     int testIndex = startIndex;
 
     // Check each vector row to make sure we have the correct fields
-    for (HashMap<String, ByteIterator> result: resultVector) {
+    for (HashMap<String, ByteIterator> result : resultVector) {
       assertEquals("Assert that this row has the correct number of fields", fieldSet.size(), result.size());
-      for (String field: fieldSet) {
-        assertEquals("Assert this field is correct in this row", keyMap.get(KEY_PREFIX + testIndex).get(field).toString(), result.get(field).toString());
+      for (String field : fieldSet) {
+        assertEquals("Assert this field is correct in this row", keyMap.get(KEY_PREFIX + testIndex).get(field).toString(),
+            result.get(field).toString());
       }
       testIndex++;
     }
diff --git a/pom.xml b/pom.xml
index 585bf584..9a49531f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,7 +82,7 @@ LICENSE file.
     <!--<mapkeeper.version>1.0</mapkeeper.version>-->
     <mongodb.version>3.0.3</mongodb.version>
     <mongodb.async.version>2.0.1</mongodb.async.version>
-    <orientdb.version>2.1.8</orientdb.version>
+    <orientdb.version>2.2.10</orientdb.version>
     <redis.version>2.0.0</redis.version>
     <s3.version>1.10.20</s3.version>
     <voldemort.version>0.81</voldemort.version>
-- 
GitLab