From e4de1c9711f1cd980c102ca7fd2e55c9314d14c8 Mon Sep 17 00:00:00 2001
From: Yuta Namiki <y.namiki@gmail.com>
Date: Thu, 9 Feb 2012 19:30:15 +0900
Subject: [PATCH] Oracle NoSQL Database client

---
 Makefile                                      |   8 +
 build.xml                                     |   5 +
 db/nosqldb/lib/README                         |  38 +++
 db/nosqldb/nosqldb.properties                 |  28 +++
 db/nosqldb/script.txt                         |   9 +
 .../src/com/yahoo/ycsb/db/NoSqlDbClient.java  | 221 ++++++++++++++++++
 6 files changed, 309 insertions(+)
 create mode 100644 db/nosqldb/lib/README
 create mode 100644 db/nosqldb/nosqldb.properties
 create mode 100644 db/nosqldb/script.txt
 create mode 100644 db/nosqldb/src/com/yahoo/ycsb/db/NoSqlDbClient.java

diff --git a/Makefile b/Makefile
index c24b9739..5970c512 100644
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,8 @@ MAPKEEPER_DIR=db/mapkeeper/lib
 MAPKEEPER_FILE=mapkeeper.jar
 GEMFIRE_DIR=db/gemfire/lib
 GEMFIRE_FILE=gemfire-6.6.1.jar
+NOSQLDB_DIR=db/nosqldb/lib
+NOSQLDB_FILE=kv-ce-1.2.123.tar.gz
 
 .PHONY: build
 build: download-database-deps
@@ -46,6 +48,7 @@ download-database-deps:  $(CASSANDRA_5_DIR)/$(CASSANDRA_5_FILE) \
 			 $(VOLDEMORT_DIR)/$(VOLDEMORT_FILE)   \
 			 $(MAPKEEPER_DIR)/$(MAPKEEPER_FILE)   \
 			 $(GEMFIRE_DIR)/$(GEMFIRE_FILE)   \
+			 $(NOSQLDB_DIR)/$(NOSQLDB_FILE)   \
 
 $(CASSANDRA_5_DIR)/$(CASSANDRA_5_FILE) :
 	wget http://archive.apache.org/dist/cassandra/0.5.1/$(CASSANDRA_5_FILE)\
@@ -105,5 +108,10 @@ $(GEMFIRE_DIR)/$(GEMFIRE_FILE) :
 	wget http://dist.gemstone.com.s3.amazonaws.com/maven/release/com/gemstone/gemfire/gemfire/6.6.1/$(GEMFIRE_FILE) \
 		 -O $(GEMFIRE_DIR)/$(GEMFIRE_FILE)
 
+$(NOSQLDB_DIR)/$(NOSQLDB_FILE) :
+	wget http://download.oracle.com/otn/nosql-database/$(NOSQLDB_FILE) \
+		 -O $(NOSQLDB_DIR)/$(NOSQLDB_FILE)
+	tar -C $(NOSQLDB_DIR) -zxf $(NOSQLDB_DIR)/$(NOSQLDB_FILE)
+
 
 
diff --git a/build.xml b/build.xml
index e5bb6cc3..7d3d974f 100644
--- a/build.xml
+++ b/build.xml
@@ -80,6 +80,11 @@
             <antcall target="dbcompile"/>
      </target>
 
+    <target name="dbcompile-nosqldb" depends="compile">
+      <property name="db.dir" value="db/nosqldb"/>
+      <antcall target="dbcompile"/>
+    </target>
+
    <target name="compile">
         <mkdir dir="${classes.dir}"/>
         <javac includeantruntime="false" srcdir="${src.dir}" destdir="${classes.dir}" classpathref="build.classpath" deprecation="on">
diff --git a/db/nosqldb/lib/README b/db/nosqldb/lib/README
new file mode 100644
index 00000000..83586631
--- /dev/null
+++ b/db/nosqldb/lib/README
@@ -0,0 +1,38 @@
+This directory should contain kvclient-1.2.123.jar for building
+and running Oracle NoSQL Database client.
+
+BUILD
+
+Oracle NoSQL Database client can be compiled using target:
+$ ant dbcompile-nosqldb
+
+CONFIGURE
+
+$KVHOME is Oracle NoSQL Database package files.
+$KVROOT is a data directory.
+
+$ mkdir $KVROOT
+$ java -jar $KVHOME/lib/kvstore-1.2.123.jar makebootconfig \
+	-root $KVROOT -port 5000 -admin 5001 -host localhost \
+	-harange 5010,5020
+$ java -jar $KVHOME/lib/kvstore-1.2.123.jar start -root $KVROOT
+$ java -jar $KVHOME/lib/kvstore-1.2.123.jar runadmin \
+	-port 5000 -host localhost -script ../script.txt
+
+BENCHMARK
+
+$ java -cp build/ycsb.jar:db/nosqldb/lib/kvclient-1.2.123.jar \
+	com.yahoo.ycsb.Client -db com.yahoo.ycsb.db.NoSqlDbClient \
+	-P workloads/workloada
+
+PROPERTIES
+
+See nosqldb.properties.
+
+STOP
+
+$ java -jar $KVHOME/lib/kvstore-1.2.123.jar stop -root $KVROOT
+
+
+Please refer to Oracle NoSQL Database docs here:
+http://docs.oracle.com/cd/NOSQL/html/index.html
diff --git a/db/nosqldb/nosqldb.properties b/db/nosqldb/nosqldb.properties
new file mode 100644
index 00000000..22f2504e
--- /dev/null
+++ b/db/nosqldb/nosqldb.properties
@@ -0,0 +1,28 @@
+#
+# Sample property file for Oracle NoSQL Database client
+#
+# Refer to the Javadoc of oracle.kv.KVStoreConfig class
+# for more details.
+#
+
+# Store name
+#storeName=kvstore
+
+# Comma-separated list of helper host/port pairs
+#helperHost=localhost:5000
+
+# Read consistency
+# "ABSOLUTE" or "NONE_REQUIRED"
+#consistency=NONE_REQUIRED
+
+# Write durability
+# "COMMIT_NO_SYNC", "COMMIT_SYNC" or "COMMIT_WRITE_NO_SYNC"
+#durability=COMMIT_NO_SYNC
+
+# Limitations on the number of active requests to a node
+#requestLimit.maxActiveRequests=100
+#requestLimit.requestThresholdPercent=90
+#requestLimit.nodeLimitPercent=80
+
+# Request timeout in seconds (positive integer)
+#requestTimeout=5
diff --git a/db/nosqldb/script.txt b/db/nosqldb/script.txt
new file mode 100644
index 00000000..87f1c8af
--- /dev/null
+++ b/db/nosqldb/script.txt
@@ -0,0 +1,9 @@
+# Simple configuration file; only one node in a system
+configure kvstore
+plan -execute -name "Deploy DC" deploy-datacenter "Local"
+plan -execute -name "Deploy n01" deploy-sn 1 localhost 5000
+plan -execute -name "Deploy admin" deploy-admin 1 5001
+addpool LocalPool
+joinpool LocalPool 1
+plan -execute -name "Deploy the store" deploy-store LocalPool 1 100
+quit
diff --git a/db/nosqldb/src/com/yahoo/ycsb/db/NoSqlDbClient.java b/db/nosqldb/src/com/yahoo/ycsb/db/NoSqlDbClient.java
new file mode 100644
index 00000000..a19d8f8f
--- /dev/null
+++ b/db/nosqldb/src/com/yahoo/ycsb/db/NoSqlDbClient.java
@@ -0,0 +1,221 @@
+package com.yahoo.ycsb.db;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.Vector;
+import java.util.concurrent.TimeUnit;
+
+import oracle.kv.Consistency;
+import oracle.kv.Durability;
+import oracle.kv.FaultException;
+import oracle.kv.KVStore;
+import oracle.kv.KVStoreConfig;
+import oracle.kv.KVStoreFactory;
+import oracle.kv.Key;
+import oracle.kv.RequestLimitConfig;
+import oracle.kv.Value;
+import oracle.kv.ValueVersion;
+
+import com.yahoo.ycsb.ByteArrayByteIterator;
+import com.yahoo.ycsb.ByteIterator;
+import com.yahoo.ycsb.DB;
+import com.yahoo.ycsb.DBException;
+
+/**
+ * A database interface layer for Oracle NoSQL Database.
+ */
+public class NoSqlDbClient extends DB {
+	
+	public static final int OK = 0;
+	public static final int ERROR = -1;
+	
+	KVStore store;
+	
+	private int getPropertyInt(Properties properties, String key, int defaultValue) throws DBException {
+		String p = properties.getProperty(key);
+		int i = defaultValue;
+		if (p != null) {
+			try {
+				i = Integer.parseInt(p);
+			} catch (NumberFormatException e) {
+				throw new DBException("Illegal number format in " + key + " property");
+			}
+		}
+		return i;
+	}
+
+	@Override
+	public void init() throws DBException {
+		Properties properties = getProperties();
+		
+		/* Mandatory properties */
+		String storeName = properties.getProperty("storeName", "kvstore");
+		String[] helperHosts = properties.getProperty("helperHost", "localhost:5000").split(",");
+		
+		KVStoreConfig config = new KVStoreConfig(storeName, helperHosts);
+		
+		/* Optional properties */
+		String p;
+		
+		p = properties.getProperty("consistency");
+		if (p != null) {
+			if (p.equalsIgnoreCase("ABSOLUTE")) {
+				config.setConsistency(Consistency.ABSOLUTE);
+			} else if (p.equalsIgnoreCase("NONE_REQUIRED")) {
+				config.setConsistency(Consistency.NONE_REQUIRED);
+			} else {
+				throw new DBException("Illegal value in consistency property");
+			}
+		}
+		
+		p = properties.getProperty("durability");
+		if (p != null) {
+			if (p.equalsIgnoreCase("COMMIT_NO_SYNC")) {
+				config.setDurability(Durability.COMMIT_NO_SYNC);
+			} else if (p.equalsIgnoreCase("COMMIT_SYNC")) {
+				config.setDurability(Durability.COMMIT_SYNC);
+			} else if (p.equalsIgnoreCase("COMMIT_WRITE_NO_SYNC")) {
+				config.setDurability(Durability.COMMIT_WRITE_NO_SYNC);
+			} else {
+				throw new DBException("Illegal value in durability property");
+			}
+		}
+		
+		int maxActiveRequests = getPropertyInt(properties,
+				"requestLimit.maxActiveRequests", RequestLimitConfig.DEFAULT_MAX_ACTIVE_REQUESTS);
+		int requestThresholdPercent = getPropertyInt(properties,
+				"requestLimit.requestThresholdPercent", RequestLimitConfig.DEFAULT_REQUEST_THRESHOLD_PERCENT);
+		int nodeLimitPercent = getPropertyInt(properties,
+				"requestLimit.nodeLimitPercent", RequestLimitConfig.DEFAULT_NODE_LIMIT_PERCENT);
+		RequestLimitConfig requestLimitConfig;
+		/* It is said that the constructor could throw NodeRequestLimitException in Javadoc, the exception is not provided */
+//		try {
+			requestLimitConfig = new RequestLimitConfig(maxActiveRequests, requestThresholdPercent, nodeLimitPercent);
+//		} catch (NodeRequestLimitException e) {
+//			throw new DBException(e);
+//		}
+		config.setRequestLimit(requestLimitConfig);
+
+		p = properties.getProperty("requestTimeout");
+		if (p != null) {
+			long timeout = 1;
+			try {
+				timeout = Long.parseLong(p); 
+			} catch (NumberFormatException e) {
+				throw new DBException("Illegal number format in requestTimeout property");
+			}
+			try {
+				// TODO Support other TimeUnit
+				config.setRequestTimeout(timeout, TimeUnit.SECONDS);
+			} catch (IllegalArgumentException e) {
+				throw new DBException(e);
+			}
+		}
+		
+		try {
+			store = KVStoreFactory.getStore(config);
+		} catch (FaultException e) {
+			throw new DBException(e);
+		}
+	}
+
+	@Override
+	public void cleanup() throws DBException {
+		store.close();
+	}
+	
+	/**
+	 * Create a key object.
+	 * We map "table" and (YCSB's) "key" to a major component of the oracle.kv.Key,
+	 * and "field" to a minor component.
+	 * 
+	 * @return An oracle.kv.Key object.
+	 */
+	private static Key createKey(String table, String key, String field) {
+		List<String> majorPath = new ArrayList<String>();
+		majorPath.add(table);
+		majorPath.add(key);
+		if (field == null) {
+			return Key.createKey(majorPath);
+		}
+		
+		return Key.createKey(majorPath, field);
+	}
+	
+	private static Key createKey(String table, String key) {
+		return createKey(table, key, null);
+	}
+	
+	private static String getFieldFromKey(Key key) {
+		return key.getMinorPath().get(0);
+	}
+
+	@Override
+	public int read(String table, String key, Set<String> fields, HashMap<String, ByteIterator> result) {
+		Key kvKey = createKey(table, key);
+		SortedMap<Key, ValueVersion> kvResult;
+		try {
+			kvResult = store.multiGet(kvKey, null, null);
+		} catch (FaultException e) {
+			System.err.println(e);
+			return ERROR;
+		}
+		
+		for (Map.Entry<Key, ValueVersion> entry : kvResult.entrySet()) {
+			/* If fields is null, read all fields */
+			String field = getFieldFromKey(entry.getKey());
+			if (fields != null && !fields.contains(field)) {
+				continue;
+			}
+			result.put(field, new ByteArrayByteIterator(entry.getValue().getValue().getValue()));
+		}
+		
+		return OK;
+	}
+
+	@Override
+	public int scan(String table, String startkey, int recordcount, Set<String> fields, Vector<HashMap<String, ByteIterator>> result) {
+		System.err.println("Oracle NoSQL Database does not support Scan semantics");
+		return ERROR;
+	}
+
+	@Override
+	public int update(String table, String key, HashMap<String, ByteIterator> values) {
+		for (Map.Entry<String, ByteIterator> entry : values.entrySet()) {
+			Key kvKey = createKey(table, key, entry.getKey());
+			Value kvValue = Value.createValue(entry.getValue().toArray());
+			try {
+				store.put(kvKey, kvValue);
+			} catch (FaultException e) {
+				System.err.println(e);
+				return ERROR;
+			}
+		}
+		
+		return OK;
+	}
+
+	@Override
+	public int insert(String table, String key, HashMap<String, ByteIterator> values) {
+		return update(table, key, values);
+	}
+
+	@Override
+	public int delete(String table, String key) {
+		Key kvKey = createKey(table, key);
+		try {
+			store.multiDelete(kvKey, null, null);
+		} catch (FaultException e) {
+			System.err.println(e);
+			return ERROR;
+		}
+		
+		return OK;
+	}
+
+}
-- 
GitLab