From 3b9e207c814f0efa2f6267abe8463b9b18e42956 Mon Sep 17 00:00:00 2001 From: "Robert J. Moore" <Robert.J.Moore@allanbank.com> Date: Wed, 7 May 2014 22:01:49 -0400 Subject: [PATCH] Update the MongoDB drivers to just use a connection string or MongoDB URI to configure the client. Old properties are deprecated and but will be added to the URI if present and the URI does not already contain a value for the setting. --- mongodb/README.md | 33 +- .../com/yahoo/ycsb/db/AsyncMongoDbClient.java | 97 ++---- .../java/com/yahoo/ycsb/db/MongoDbClient.java | 306 ++++++++---------- 3 files changed, 171 insertions(+), 265 deletions(-) diff --git a/mongodb/README.md b/mongodb/README.md index a415243e..cefec635 100644 --- a/mongodb/README.md +++ b/mongodb/README.md @@ -77,34 +77,19 @@ See the next section for the list of configuration parameters for MongoDB. ## MongoDB Configuration Parameters -- `mongodb.url` default: `mongodb://localhost:27017` +- `mongodb.url` default: `mongodb://localhost:27017/ycsb?w=1` + - This should be a MongoDB URI or connection string. + - See http://docs.mongodb.org/manual/reference/connection-string/ for the standard options. + - For the complete set of options for the asynchronous driver see: + - http://www.allanbank.com/mongodb-async-driver/apidocs/index.html?com/allanbank/mongodb/MongoDbUri.html + - For the complete set of options for the synchronous driver see: + - http://api.mongodb.org/java/current/index.html?com/mongodb/MongoClientURI.html -- `mongodb.database` default: `ycsb` - -- `mongodb.writeConcern` default `acknowledged` - - options are : - - `errors_ignored` - - `unacknowledged` - - `acknowledged` - - `journaled` - - `replica_acknowledged` - -- `mongodb.readPreference` default `primary` - - options are : - - `primary` - - `primary_preferred` - - `secondary` - - `secondary_preferred` - - `nearest` - -- `mongodb.maxconnections` (default `100`) - -- `mongodb.threadsAllowedToBlockForConnectionMultiplier` (default `5`) For example: - ./bin/ycsb load mongodb-async -s -P workloads/workloada -p mongodb.writeConcern=unacknowledged + ./bin/ycsb load mongodb-async -s -P workloads/workloada -p mongodb.url=mongodb://localhost:27017/ycsb?w=0 To run with the synchronous driver from MongoDB Inc.: - ./bin/ycsb load mongodb -s -P workloads/workloada -p mongodb.writeConcern=unacknowledged + ./bin/ycsb load mongodb -s -P workloads/workloada -p mongodb.url=mongodb://localhost:27017/ycsb?w=0 diff --git a/mongodb/src/main/java/com/yahoo/ycsb/db/AsyncMongoDbClient.java b/mongodb/src/main/java/com/yahoo/ycsb/db/AsyncMongoDbClient.java index 27aed8aa..1115987f 100644 --- a/mongodb/src/main/java/com/yahoo/ycsb/db/AsyncMongoDbClient.java +++ b/mongodb/src/main/java/com/yahoo/ycsb/db/AsyncMongoDbClient.java @@ -19,6 +19,7 @@ import java.util.concurrent.atomic.AtomicInteger; import com.allanbank.mongodb.Durability; import com.allanbank.mongodb.LockType; import com.allanbank.mongodb.MongoClient; +import com.allanbank.mongodb.MongoClientConfiguration; import com.allanbank.mongodb.MongoCollection; import com.allanbank.mongodb.MongoDatabase; import com.allanbank.mongodb.MongoDbUri; @@ -126,11 +127,6 @@ public class AsyncMongoDbClient extends DB { public final void init() throws DBException { final int count = initCount.incrementAndGet(); - final Properties props = getProperties(); - final String maxConnections = props.getProperty( - "mongodb.maxconnections", "100"); - final int connections = Integer.parseInt(maxConnections); - synchronized (AsyncMongoDbClient.class) { if (mongo != null) { db = mongo.getDatabase(database); @@ -138,78 +134,51 @@ public class AsyncMongoDbClient extends DB { // If there are more threads (count) than connections then the // Low latency spin lock is not really needed as we will keep // the connections occupied. - if (count > connections) { + if (count > mongo.getConfig().getMaxConnectionCount()) { mongo.getConfig().setLockType(LockType.MUTEX); } return; } - // initialize MongoDb driver + // Just use the standard connection format URL + // http://docs.mongodb.org/manual/reference/connection-string/ + final Properties props = getProperties(); String url = props.getProperty("mongodb.url", - "mongodb://localhost:27017"); - database = props.getProperty("mongodb.database", "ycsb"); - String writeConcernType = props.getProperty("mongodb.writeConcern", - props.getProperty("mongodb.durability", "acknowledged")) - .toLowerCase(); - - if ("errors_ignored".equals(writeConcernType)) { - writeConcern = Durability.NONE; - } - else if ("unacknowledged".equals(writeConcernType)) { - writeConcern = Durability.NONE; - } - else if ("acknowledged".equals(writeConcernType)) { - writeConcern = Durability.ACK; - } - else if ("journaled".equals(writeConcernType)) { - writeConcern = Durability.journalDurable(0); - } - else if ("replica_acknowledged".equals(writeConcernType)) { - writeConcern = Durability.replicaDurable(2, 0); - } - else { + "mongodb://localhost:27017/ycsb?w=1"); + if (!url.startsWith("mongodb://")) { System.err - .println("ERROR: Invalid writeConcern: '" - + writeConcernType - + "'. " - + "Must be [ errors_ignored | unacknowledged | acknowledged | journaled | replica_acknowledged ]"); + .println("ERROR: Invalid URL: '" + + url + + "'. Must be of the form " + + "'mongodb://<host1>:<port1>,<host2>:<port2>/database?options'. " + + "See http://docs.mongodb.org/manual/reference/connection-string/."); System.exit(1); } - // readPreference - String readPreferenceType = props.getProperty( - "mongodb.readPreference", "primary").toLowerCase(); - if ("primary".equals(readPreferenceType)) { - readPreference = ReadPreference.primary(); - } - else if ("primary_preferred".equals(readPreferenceType)) { - readPreference = ReadPreference.preferPrimary(); - } - else if ("secondary".equals(readPreferenceType)) { - readPreference = ReadPreference.secondary(); - } - else if ("secondary_preferred".equals(readPreferenceType)) { - readPreference = ReadPreference.preferSecondary(); - } - else if ("nearest".equals(readPreferenceType)) { - readPreference = ReadPreference.closest(); - } - else { - System.err - .println("ERROR: Invalid readPreference: '" - + readPreferenceType - + "'. Must be [ primary | primary_preferred | secondary | secondary_preferred | nearest ]"); - System.exit(1); - } + MongoDbUri uri = new MongoDbUri(url); try { - // need to append db to url. - url += "/" + database; - System.out.println("new database url = " + url); - mongo = MongoFactory.createClient(new MongoDbUri(url)); - mongo.getConfig().setMaxConnectionCount(connections); - mongo.getConfig().setLockType(LockType.LOW_LATENCY_SPIN); // assumed... + database = uri.getDatabase(); + if ((database == null) || database.isEmpty()) { + System.err + .println("ERROR: Invalid URL: '" + + url + + "'. Must provide a database name with the URI. " + + "'mongodb://<host1>:<port1>,<host2>:<port2>/database"); + System.exit(1); + } + + mongo = MongoFactory.createClient(uri); + + MongoClientConfiguration config = mongo.getConfig(); + if (!url.toLowerCase().contains("locktype=")) { + config.setLockType(LockType.LOW_LATENCY_SPIN); // assumed... + } + + readPreference = config.getDefaultReadPreference(); + writeConcern = config.getDefaultDurability(); + db = mongo.getDatabase(database); System.out.println("mongo connection created with " + url); diff --git a/mongodb/src/main/java/com/yahoo/ycsb/db/MongoDbClient.java b/mongodb/src/main/java/com/yahoo/ycsb/db/MongoDbClient.java index d3c3a2b4..c7f34956 100644 --- a/mongodb/src/main/java/com/yahoo/ycsb/db/MongoDbClient.java +++ b/mongodb/src/main/java/com/yahoo/ycsb/db/MongoDbClient.java @@ -19,12 +19,11 @@ import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; import com.mongodb.BasicDBObject; -import com.mongodb.DBAddress; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.MongoClient; -import com.mongodb.MongoClientOptions; +import com.mongodb.MongoClientURI; import com.mongodb.ReadPreference; import com.mongodb.WriteConcern; import com.yahoo.ycsb.ByteArrayByteIterator; @@ -32,7 +31,6 @@ import com.yahoo.ycsb.ByteIterator; import com.yahoo.ycsb.DB; import com.yahoo.ycsb.DBException; - /** * MongoDB client for YCSB framework. * @@ -48,15 +46,6 @@ public class MongoDbClient extends DB { /** Used to include a field in a response. */ protected static final Integer INCLUDE = Integer.valueOf(1); - /** A singleton Mongo instance. */ - private static MongoClient mongo; - - /** The default write concern for the test. */ - private static WriteConcern writeConcern; - - /** The default read preference for the test */ - private static ReadPreference readPreference; - /** The database to access. */ private static String database; @@ -66,125 +55,14 @@ public class MongoDbClient extends DB { */ private static final AtomicInteger initCount = new AtomicInteger(0); - /** - * Initialize any state for this DB. Called once per DB instance; there is - * one DB instance per client thread. - */ - @Override - public void init() throws DBException { - initCount.incrementAndGet(); - synchronized (INCLUDE) { - if (mongo != null) { - return; - } - - // initialize MongoDb driver - Properties props = getProperties(); - String url = props.getProperty("mongodb.url", - "mongodb://localhost:27017"); - - if (url.contains(",")) { - //pick one and random - String[] urls = url.split(","); - int index = new Random().nextInt(urls.length); - url = urls[index]; - System.out.printf("Using Mongo URL: %s\n", url); - } - - database = props.getProperty("mongodb.database", "ycsb"); - - final String maxConnections = props.getProperty( - "mongodb.maxconnections", "100"); - final String threadsAllowedToBlockForConnectionMultiplier = props - .getProperty( - "mongodb.threadsAllowedToBlockForConnectionMultiplier", - "5"); - - // write concern - String writeConcernType = props.getProperty("mongodb.writeConcern", - "acknowledged").toLowerCase(); - if ("errors_ignored".equals(writeConcernType)) { - writeConcern = WriteConcern.ERRORS_IGNORED; - } - else if ("unacknowledged".equals(writeConcernType)) { - writeConcern = WriteConcern.UNACKNOWLEDGED; - } - else if ("acknowledged".equals(writeConcernType)) { - writeConcern = WriteConcern.ACKNOWLEDGED; - } - else if ("journaled".equals(writeConcernType)) { - writeConcern = WriteConcern.JOURNALED; - } - else if ("replica_acknowledged".equals(writeConcernType)) { - writeConcern = WriteConcern.REPLICA_ACKNOWLEDGED; - } - else { - System.err - .println("ERROR: Invalid writeConcern: '" - + writeConcernType - + "'. " - + "Must be [ errors_ignored | unacknowledged | acknowledged | journaled | replica_acknowledged ]"); - System.exit(1); - } - - // readPreference - String readPreferenceType = props.getProperty( - "mongodb.readPreference", "primary").toLowerCase(); - if ("primary".equals(readPreferenceType)) { - readPreference = ReadPreference.primary(); - } - else if ("primary_preferred".equals(readPreferenceType)) { - readPreference = ReadPreference.primaryPreferred(); - } - else if ("secondary".equals(readPreferenceType)) { - readPreference = ReadPreference.secondary(); - } - else if ("secondary_preferred".equals(readPreferenceType)) { - readPreference = ReadPreference.secondaryPreferred(); - } - else if ("nearest".equals(readPreferenceType)) { - readPreference = ReadPreference.nearest(); - } - else { - System.err - .println("ERROR: Invalid readPreference: '" - + readPreferenceType - + "'. Must be [ primary | primary_preferred | secondary | secondary_preferred | nearest ]"); - System.exit(1); - } - - try { - // strip out prefix since Java driver doesn't currently support - // standard connection format URL yet - // http://www.mongodb.org/display/DOCS/Connections - if (url.startsWith("mongodb://")) { - url = url.substring(10); - - } + /** A singleton Mongo instance. */ + private static MongoClient mongo; - // need to append db to url. - url += "/" + database; - System.out.println("new database url = " + url); - MongoClientOptions options = MongoClientOptions - .builder() - .cursorFinalizerEnabled(false) - .connectionsPerHost(Integer.parseInt(maxConnections)) - .threadsAllowedToBlockForConnectionMultiplier( - Integer.parseInt(threadsAllowedToBlockForConnectionMultiplier)) - .build(); - mongo = new MongoClient(new DBAddress(url), options); + /** The default read preference for the test */ + private static ReadPreference readPreference; - System.out.println("mongo connection created with " + url); - } - catch (Exception e1) { - System.err - .println("Could not initialize MongoDB connection pool for Loader: " - + e1.toString()); - e1.printStackTrace(); - return; - } - } - } + /** The default write concern for the test. */ + private static WriteConcern writeConcern; /** * Cleanup any state for this DB. Called once per DB instance; there is one @@ -237,6 +115,80 @@ public class MongoDbClient extends DB { } } + /** + * Initialize any state for this DB. Called once per DB instance; there is + * one DB instance per client thread. + */ + @Override + public void init() throws DBException { + initCount.incrementAndGet(); + synchronized (INCLUDE) { + if (mongo != null) { + return; + } + + // Just use the standard connection format URL + // http://docs.mongodb.org/manual/reference/connection-string/ + // + // Support legacy options by updating the URL as appropriate. + Properties props = getProperties(); + String url = props.getProperty("mongodb.url", null); + boolean defaultedUrl = false; + if (url == null) { + defaultedUrl = true; + url = "mongodb://localhost:27017/ycsb?w=1"; + } + + url = OptionsSupport.updateUrl(url, props); + + if (!url.startsWith("mongodb://")) { + System.err + .println("ERROR: Invalid URL: '" + + url + + "'. Must be of the form " + + "'mongodb://<host1>:<port1>,<host2>:<port2>/database?options'. " + + "See http://docs.mongodb.org/manual/reference/connection-string/."); + System.exit(1); + } + + try { + MongoClientURI uri = new MongoClientURI(url); + + String uriDb = uri.getDatabase(); + if (!defaultedUrl && (uriDb != null) && !uriDb.isEmpty() + && !"admin".equals(uriDb)) { + database = uriDb; + } + else { + database = props.getProperty("mongodb.database", "ycsb"); + } + + if ((database == null) || database.isEmpty()) { + System.err + .println("ERROR: Invalid URL: '" + + url + + "'. Must provide a database name with the URI. " + + "'mongodb://<host1>:<port1>,<host2>:<port2>/database"); + System.exit(1); + } + + readPreference = uri.getOptions().getReadPreference(); + writeConcern = uri.getOptions().getWriteConcern(); + + mongo = new MongoClient(uri); + + System.out.println("mongo connection created with " + url); + } + catch (Exception e1) { + System.err + .println("Could not initialize MongoDB connection pool for Loader: " + + e1.toString()); + e1.printStackTrace(); + return; + } + } + } + /** * Insert a record in the database. Any field/value pairs in the specified * values HashMap will be written into the record with the specified record @@ -340,54 +292,6 @@ public class MongoDbClient extends DB { } } - /** - * Update a record in the database. Any field/value pairs in the specified - * values HashMap will be written into the record with the specified record - * key, overwriting any existing values with the same field name. - * - * @param table - * The name of the table - * @param key - * The record key of the record to write. - * @param values - * A HashMap of field/value pairs to update in the record - * @return Zero on success, a non-zero error code on error. See this class's - * description for a discussion of error codes. - */ - @Override - public int update(String table, String key, - HashMap<String, ByteIterator> values) { - com.mongodb.DB db = null; - try { - db = mongo.getDB(database); - - db.requestStart(); - - DBCollection collection = db.getCollection(table); - DBObject q = new BasicDBObject().append("_id", key); - DBObject u = new BasicDBObject(); - DBObject fieldsToSet = new BasicDBObject(); - Iterator<String> keys = values.keySet().iterator(); - while (keys.hasNext()) { - String tmpKey = keys.next(); - fieldsToSet.put(tmpKey, values.get(tmpKey).toArray()); - - } - u.put("$set", fieldsToSet); - collection.update(q, u, false, false, writeConcern); - return 0; - } - catch (Exception e) { - System.err.println(e.toString()); - return 1; - } - finally { - if (db != null) { - db.requestDone(); - } - } - } - /** * Perform a range scan for a set of records in the database. Each * field/value pair from the result will be stored in a HashMap. @@ -448,6 +352,54 @@ public class MongoDbClient extends DB { } + /** + * Update a record in the database. Any field/value pairs in the specified + * values HashMap will be written into the record with the specified record + * key, overwriting any existing values with the same field name. + * + * @param table + * The name of the table + * @param key + * The record key of the record to write. + * @param values + * A HashMap of field/value pairs to update in the record + * @return Zero on success, a non-zero error code on error. See this class's + * description for a discussion of error codes. + */ + @Override + public int update(String table, String key, + HashMap<String, ByteIterator> values) { + com.mongodb.DB db = null; + try { + db = mongo.getDB(database); + + db.requestStart(); + + DBCollection collection = db.getCollection(table); + DBObject q = new BasicDBObject().append("_id", key); + DBObject u = new BasicDBObject(); + DBObject fieldsToSet = new BasicDBObject(); + Iterator<String> keys = values.keySet().iterator(); + while (keys.hasNext()) { + String tmpKey = keys.next(); + fieldsToSet.put(tmpKey, values.get(tmpKey).toArray()); + + } + u.put("$set", fieldsToSet); + collection.update(q, u, false, false, writeConcern); + return 0; + } + catch (Exception e) { + System.err.println(e.toString()); + return 1; + } + finally { + if (db != null) { + db.requestDone(); + } + } + } + /** * Fills the map with the values from the DBObject. * -- GitLab