Skip to content

Commit dad357a

Browse files
committed
Add work-in-progress java bindings for RAMCloud, as well as a client for use with
YCSB (Yahoo! Cloud Storage Benchmark). Both are in working order, though RejectRule support hasn't been added to the java bindings yet. Using JRamCloud I see about 2-3us additional latency per small read. The YCSB interface adds another 2-3us on top of that, mostly because we have to emulate having distinct named fields in each object.
1 parent a89b9cb commit dad357a

File tree

3 files changed

+1175
-0
lines changed

3 files changed

+1175
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
/* Copyright (c) 2013 Stanford University
2+
*
3+
* Permission to use, copy, modify, and distribute this software for any
4+
* purpose with or without fee is hereby granted, provided that the above
5+
* copyright notice and this permission notice appear in all copies.
6+
*
7+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
8+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
10+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14+
*/
15+
16+
package edu.stanford.ramcloud;
17+
18+
/*
19+
* This class provides Java bindings for RAMCloud. Right now it is a rather
20+
* simple subset of what RamCloud.h defines.
21+
*
22+
* Running ``javah'' on this file will generate a C header file with the
23+
* appropriate JNI function definitions. The glue interfacing to the C++
24+
* RAMCloud library can be found in JRamCloud.cc.
25+
*
26+
* For JNI information, the IBM tutorials and Android developer docs are much
27+
* better than Sun's at giving an overall intro:
28+
* http://www.ibm.com/developerworks/java/tutorials/j-jni/section4.html
29+
* http://developer.android.com/training/articles/perf-jni.html
30+
*/
31+
public class JRamCloud {
32+
static {
33+
System.loadLibrary("edu_stanford_ramcloud_JRamCloud");
34+
}
35+
36+
/// Pointer to the underlying C++ RAMCloud object associated with this
37+
/// object.
38+
private long ramcloudObjectPointer = 0;
39+
40+
/**
41+
* See src/RejectRules.h.
42+
*/
43+
public class RejectRules {
44+
long givenVersion = -1;
45+
boolean doesntExist = false;
46+
boolean exists = false;
47+
boolean versionLeGiven = false;
48+
boolean versionNeGiven = false;
49+
}
50+
51+
/**
52+
* This class is returned by Read operations. It encapsulates the entire
53+
* object, including the key, value, and version.
54+
*
55+
* It mostly exists because Java doesn't support primitive out parameters
56+
* or multiple return values, and we don't know the object's size ahead of
57+
* time, so passing in a fixed-length array would be problematic.
58+
*/
59+
public class Object {
60+
Object(byte[] _key, byte[] _value, long _version)
61+
{
62+
key = _key;
63+
value = _value;
64+
version = _version;
65+
}
66+
67+
public String
68+
getKey()
69+
{
70+
return new String(key);
71+
}
72+
73+
public String
74+
getValue()
75+
{
76+
return new String(value);
77+
}
78+
79+
final public byte[] key;
80+
final public byte[] value;
81+
final public long version;
82+
}
83+
84+
/**
85+
* Connect to the RAMCloud cluster specified by the given coordinator's
86+
* service locator string. This causes the JNI code to instantiate the
87+
* underlying RamCloud C++ object.
88+
*/
89+
public
90+
JRamCloud(String coordinatorLocator)
91+
{
92+
ramcloudObjectPointer = connect(coordinatorLocator);
93+
}
94+
95+
/**
96+
* Disconnect from the RAMCloud cluster. This causes the JNI code to
97+
* destroy the underlying RamCloud C++ object.
98+
*/
99+
public void
100+
disconnect()
101+
{
102+
if (ramcloudObjectPointer != 0) {
103+
disconnect(ramcloudObjectPointer);
104+
ramcloudObjectPointer = 0;
105+
}
106+
}
107+
108+
/**
109+
* This method is called by the garbage collector before destroying the
110+
* object. The user really should have called disconnect, but in case
111+
* they did not, be sure to clean up after them.
112+
*/
113+
public void
114+
finalize()
115+
{
116+
System.err.println("warning: JRamCloud::disconnect() was not called " +
117+
"prior to the finalizer. You should disconnect " +
118+
"your JRamCloud object when you're done with it.");
119+
disconnect();
120+
}
121+
122+
/**
123+
* Convenience read() wrapper that take a String key argument.
124+
*/
125+
public Object
126+
read(long tableId, String key)
127+
{
128+
return read(tableId, key.getBytes());
129+
}
130+
131+
/**
132+
* Convenience read() wrapper that take a String key argument.
133+
*/
134+
public Object
135+
read(long tableId, String key, RejectRules rules)
136+
{
137+
return read(tableId, key.getBytes(), rules);
138+
}
139+
140+
/**
141+
* Convenience remove() wrapper that take a String key argument.
142+
*/
143+
public long
144+
remove(long tableId, String key)
145+
{
146+
return remove(tableId, key.getBytes());
147+
}
148+
149+
/**
150+
* Convenience remove() wrapper that take a String key argument.
151+
*/
152+
public long
153+
remove(long tableId, String key, RejectRules rules)
154+
{
155+
return remove(tableId, key.getBytes(), rules);
156+
}
157+
158+
/**
159+
* Convenience write() wrapper that take String key and value arguments.
160+
*/
161+
public long
162+
write(long tableId, String key, String value)
163+
{
164+
return write(tableId, key.getBytes(), value.getBytes());
165+
}
166+
167+
/**
168+
* Convenience write() wrapper that take String key and value arguments.
169+
*/
170+
public long
171+
write(long tableId, String key, String value, RejectRules rules)
172+
{
173+
return write(tableId, key.getBytes(), value.getBytes(), rules);
174+
}
175+
176+
/**
177+
* Convenience write() wrapper that takes a String key and a byte[] value
178+
* argument.
179+
*/
180+
public long
181+
write(long tableId, String key, byte[] value)
182+
{
183+
return write(tableId, key.getBytes(), value);
184+
}
185+
186+
/**
187+
* Convenience write() wrapper that takes a String key and a byte[] value
188+
* argument.
189+
*/
190+
public long
191+
write(long tableId, String key, byte[] value, RejectRules rules)
192+
{
193+
return write(tableId, key.getBytes(), value, rules);
194+
}
195+
196+
private static native long connect(String coordinatorLocator);
197+
private static native void disconnect(long ramcloudObjectPointer);
198+
199+
public native long createTable(String name);
200+
public native long createTable(String name, int serverSpan);
201+
public native void dropTable(String name);
202+
public native long getTableId(String name);
203+
public native Object read(long tableId, byte[] key);
204+
public native Object read(long tableId, byte[] key, RejectRules rules);
205+
public native long remove(long tableId, byte[] key);
206+
public native long remove(long tableId, byte[] key, RejectRules rules);
207+
public native long write(long tableId, byte[] key, byte[] value);
208+
public native long write(long tableId, byte[] key, byte[] value, RejectRules rules);
209+
210+
/*
211+
* The following exceptions may be thrown by the JNI functions:
212+
*/
213+
214+
public class TableDoesntExistException extends Exception {
215+
public TableDoesntExistException(String message)
216+
{
217+
super(message);
218+
}
219+
}
220+
221+
public class ObjectDoesntExistException extends Exception {
222+
public ObjectDoesntExistException(String message)
223+
{
224+
super(message);
225+
}
226+
}
227+
228+
public class ObjectExistsException extends Exception {
229+
public ObjectExistsException(String message)
230+
{
231+
super(message);
232+
}
233+
}
234+
235+
public class WrongVersionException extends Exception {
236+
public WrongVersionException(String message)
237+
{
238+
super(message);
239+
}
240+
}
241+
242+
/**
243+
* A simple end-to-end test of the java bindings.
244+
*/
245+
public static void
246+
main(String argv[])
247+
{
248+
JRamCloud ramcloud = new JRamCloud(argv[0]);
249+
long tableId = ramcloud.createTable("hi");
250+
System.out.println("created table, id = " + tableId);
251+
long tableId2 = ramcloud.getTableId("hi");
252+
System.out.println("getTableId says tableId = " + tableId2);
253+
254+
System.out.println("wrote obj version = " +
255+
ramcloud.write(tableId, "thisIsTheKey", "thisIsTheValue"));
256+
257+
JRamCloud.Object o = ramcloud.read(tableId, "thisIsTheKey");
258+
System.out.println("read object: key = [" + o.getKey() + "], value = ["
259+
+ o.getValue() + "], version = " + o.version);
260+
261+
ramcloud.remove(tableId, "thisIsTheKey");
262+
263+
try {
264+
ramcloud.read(tableId, "thisIsTheKey");
265+
System.out.println("Error: shouldn't have read successfully!");
266+
} catch (Exception e) {
267+
// OK
268+
}
269+
270+
ramcloud.write(tableId, "thisIsTheKey", "thisIsTheValue");
271+
long before = System.nanoTime();
272+
for (int i = 0; i < 100000; i++) {
273+
JRamCloud.Object unused = ramcloud.read(tableId, "thisIsTheKey");
274+
}
275+
long after = System.nanoTime();
276+
System.out.println("Avg read latency: " +
277+
((double)(after - before) / 100000 / 1000) + " usec");
278+
}
279+
}

0 commit comments

Comments
 (0)