From 6116744dc269afcd4061c2b047d2c8135fc70bda Mon Sep 17 00:00:00 2001
From: Andy Kruth <kruthar@gmail.com>
Date: Tue, 2 Feb 2016 08:41:19 -0600
Subject: [PATCH] [orientdb] put db conn stuff in sync method

All db connection and creation logic is the same, it is just pulled into
a synchronized method so that multiple threads run through the
connection process one at a time.

Fixes #571
---
 orientdb/README.md                            |  9 +++
 .../com/yahoo/ycsb/db/OrientDBClient.java     | 80 ++++++++++++-------
 2 files changed, 61 insertions(+), 28 deletions(-)

diff --git a/orientdb/README.md b/orientdb/README.md
index ba79159f..09d6baf6 100644
--- a/orientdb/README.md
+++ b/orientdb/README.md
@@ -39,6 +39,15 @@ Then, run the workload:
 
 See the next section for the list of configuration parameters for OrientDB.
 
+## DB creation with the OrientDBClient
+This client will create a database for you if the connection database you specify does not exists. You can also specify connection information to a preexisting database.
+
+You can use the ```orientdb.newdb=true``` property to allow this client to drop and create a new database instance during the ```load``` phase.
+
+NOTE: understand that using the ```orientdb.newdb=true``` property will drop and recreate databases even if it was a preexisting instance.
+
+WARNING: Creating a new database will be done safely with multiple threads on a single YCSB instance, but is not guaranteed to work when launching multiple YCSB instances. In that scenario it is suggested that you create the db before hand, or run the ```load``` phase with a single YCSB instance.
+
 ## OrientDB Configuration Parameters
 
 * ```orientdb.url``` - (required) The address to your database.
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 525b2a95..b7763228 100644
--- a/orientdb/src/main/java/com/yahoo/ycsb/db/OrientDBClient.java
+++ b/orientdb/src/main/java/com/yahoo/ycsb/db/OrientDBClient.java
@@ -80,30 +80,35 @@ public class OrientDBClient extends DB {
   private static final String ORIENTDB_MASSIVEREAD = "massiveread";
   private static final String ORIENTDB_NOCACHE = "nocache";
 
-  private final Logger log = LoggerFactory.getLogger(getClass());
-
-  @Override
-  public void init() throws DBException {
-    Properties props = getProperties();
+  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
+   */
+  private static synchronized ODatabaseDocumentTx initDB(Properties props) throws DBException {
     String url = props.getProperty(URL_PROPERTY);
     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);
-    String intent = props.getProperty(INTENT_PROPERTY, INTENT_PROPERTY_DEFAULT);
-    Boolean dotransactions = Boolean.parseBoolean(
-        props.getProperty(DO_TRANSACTIONS_PROPERTY, DO_TRANSACTIONS_PROPERTY_DEFAULT));
+    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);
+    LOG.info("OrientDB loading database url = " + url);
 
     // If using a remote database, use the OServerAdmin interface to connect
     if (url.startsWith(OEngineRemote.NAME)) {
-      isRemote = true;
       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);
@@ -113,54 +118,73 @@ public class OrientDBClient extends DB {
         OServerAdmin server = new OServerAdmin(url).connect(user, password);
 
         if (server.existsDatabase()) {
-          if (newdb && !dotransactions) {
-            log.info("OrientDB dropping and recreating fresh db on remote server.");
+          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");
+          LOG.info("OrientDB database not found, creating fresh db");
           server.createDatabase(server.getURL(), ORIENTDB_DOCUMENT_TYPE, remoteStorageType);
         }
 
         server.close();
-        db = new ODatabaseDocumentTx(url).open(user, password);
+        dbconn = new ODatabaseDocumentTx(url).open(user, password);
       } catch (IOException | OException e) {
         throw new DBException(String.format("Error interfacing with %s", url), e);
       }
     } else {
       try {
-        db = new ODatabaseDocumentTx(url);
-        if (db.exists()) {
-          db.open(user, password);
-          if (newdb && !dotransactions) {
-            log.info("OrientDB dropping and recreating fresh db.");
-            db.drop();
-            db.create();
+        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();
           }
         } else {
-          log.info("OrientDB database not found, creating fresh db");
-          db.create();
+          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);
       }
     }
 
-    log.info("OrientDB connection created with " + url);
+    if (dbconn == null) {
+      throw new DBException("Could not establish connection to: " + url);
+    }
+
+    LOG.info("OrientDB connection created with " + url);
+    return dbconn;
+  }
+
+  @Override
+  public void init() throws DBException {
+    Properties props = getProperties();
+
+    String intent = props.getProperty(INTENT_PROPERTY, INTENT_PROPERTY_DEFAULT);
+
+    db = initDB(props);
+
+    if (db.getURL().startsWith(OEngineRemote.NAME)) {
+      isRemote = true;
+    }
+
     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.");
+      LOG.info("Declaring intent of MassiveInsert.");
       db.declareIntent(new OIntentMassiveInsert());
     } else if (intent.equals(ORIENTDB_MASSIVEREAD)) {
-      log.info("Declaring intent of MassiveRead.");
+      LOG.info("Declaring intent of MassiveRead.");
       db.declareIntent(new OIntentMassiveRead());
     } else if (intent.equals(ORIENTDB_NOCACHE)) {
-      log.info("Declaring intent of NoCache.");
+      LOG.info("Declaring intent of NoCache.");
       db.declareIntent(new OIntentNoCache());
     }
   }
@@ -248,7 +272,7 @@ public class OrientDBClient extends DB {
                      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.");
+      LOG.warn("OrientDB scan operation is not implemented for remote database connections.");
       return Status.NOT_IMPLEMENTED;
     }
 
-- 
GitLab