blob: 9241093a198039f4a757a16bb10ae3b183ece5a5 [file] [log] [blame]
Yuta HIGUCHIa1e655a2014-01-23 17:43:11 -08001/* 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
yoshi28bac132014-01-22 11:00:17 -080016package com.tinkerpop.blueprints.impls.ramcloud;
17
18import java.io.Serializable;
19import java.util.ArrayList;
20import java.util.Arrays;
21import java.util.Map;
22import java.util.HashMap;
23import java.util.Set;
24import java.util.TreeMap;
25
26import org.slf4j.Logger;
27import org.slf4j.LoggerFactory;
28
Yuta HIGUCHIdbd5a2f2014-01-23 19:01:13 -080029import com.esotericsoftware.kryo.Kryo;
30import com.esotericsoftware.kryo.io.ByteBufferInput;
31import com.esotericsoftware.kryo.io.Output;
yoshi28bac132014-01-22 11:00:17 -080032import com.tinkerpop.blueprints.Edge;
33import com.tinkerpop.blueprints.Element;
34import com.tinkerpop.blueprints.Vertex;
35import com.tinkerpop.blueprints.util.ExceptionFactory;
36import com.tinkerpop.blueprints.impls.ramcloud.PerfMon;
37
38import edu.stanford.ramcloud.JRamCloud;
39
40public class RamCloudElement implements Element, Serializable {
41
42 private final static Logger log = LoggerFactory.getLogger(RamCloudGraph.class);
43 private byte[] rcPropTableKey;
44 private long rcPropTableId;
45 private RamCloudGraph graph;
Yoshi Muroi815c7f92014-01-30 18:06:16 -080046 private long propVersion;
yoshi28bac132014-01-22 11:00:17 -080047
48 private static final ThreadLocal<Kryo> kryo = new ThreadLocal<Kryo>() {
49 @Override
50 protected Kryo initialValue() {
51 Kryo kryo = new Kryo();
52 kryo.setRegistrationRequired(true);
53 kryo.register(String.class);
54 kryo.register(Long.class);
55 kryo.register(Integer.class);
56 kryo.register(Short.class);
57 kryo.register(Byte.class);
58 kryo.register(TreeMap.class);
59 kryo.register(ArrayList.class);
60 kryo.setReferences(false);
61 return kryo;
62 }
63 };
64
65 public RamCloudElement() {
66 }
67
68 public RamCloudElement(byte[] rcPropTableKey, long rcPropTableId, RamCloudGraph graph) {
69 this.rcPropTableKey = rcPropTableKey;
70 this.rcPropTableId = rcPropTableId;
71 this.graph = graph;
72 }
73
74 protected Map<String, Object> getPropertyMap() {
75 JRamCloud.Object propTableEntry;
76
77 PerfMon pm = PerfMon.getInstance();
78 try {
79 JRamCloud vertTable = graph.getRcClient();
80 pm.read_start("RamCloudElement getPropertyMap()");
81 propTableEntry = vertTable.read(rcPropTableId, rcPropTableKey);
82 pm.read_end("RamCloudElement getPropertyMap()");
Yoshi Muroi815c7f92014-01-30 18:06:16 -080083 propVersion = propTableEntry.version;
yoshi28bac132014-01-22 11:00:17 -080084 if (propTableEntry.value.length > 1024 * 1024 * 0.9) {
85 log.warn("Element[id={}] property map size is near 1MB limit!", new String(rcPropTableKey));
86 }
87 } catch (Exception e) {
88 pm.read_end("RamCloudElement getPropertyMap()");
89 log.warn("Element does not have a property table entry!");
90 return null;
91 }
92
93 return convertRcBytesToPropertyMap(propTableEntry.value);
94 }
95
96 public static Map<String, Object> convertRcBytesToPropertyMapEx(byte[] byteArray) {
97 if (byteArray == null) {
98 log.warn("Got a null byteArray argument");
99 return null;
100 } else if (byteArray.length != 0) {
101 PerfMon pm = PerfMon.getInstance();
102 pm.deser_start("RamCloudElement convertRcBytesToPropertyMapEx()");
103 ByteBufferInput input = new ByteBufferInput(byteArray);
104 TreeMap map = kryo.get().readObject(input, TreeMap.class);
105 pm.deser_end("RamCloudElement convertRcBytesToPropertyMapEx()");
106 return map;
107 } else {
108 return new TreeMap<String, Object>();
109 }
110 }
111
112 public Map<String, Object> convertRcBytesToPropertyMap(byte[] byteArray) {
113 if (byteArray == null) {
114 log.warn("Got a null byteArray argument");
115 return null;
116 } else if (byteArray.length != 0) {
117 PerfMon pm = PerfMon.getInstance();
yoshi28bac132014-01-22 11:00:17 -0800118 pm.deser_start("RamCloudElement convertRcBytesToPropertyMap()");
119 ByteBufferInput input = new ByteBufferInput(byteArray);
120 TreeMap map = kryo.get().readObject(input, TreeMap.class);
121 pm.deser_end("RamCloudElement convertRcBytesToPropertyMap()");
yoshi28bac132014-01-22 11:00:17 -0800122 return map;
123 } else {
124 return new TreeMap<String, Object>();
125 }
126 }
127
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800128 private static byte[] convertVertexPropertyMapToRcBytes(Map<String, Object> map) {
yoshi28bac132014-01-22 11:00:17 -0800129 byte[] rcValue;
130 PerfMon pm = PerfMon.getInstance();
131
yoshi28bac132014-01-22 11:00:17 -0800132 pm.ser_start("RamCloudElement setPropertyMap()");
133 byte[] rcTemp = new byte[1024*1024];
134 Output output = new Output(rcTemp);
135 kryo.get().writeObject(output, map);
yoshi28bac132014-01-22 11:00:17 -0800136 rcValue = output.toBytes();
137 pm.ser_end("RamCloudElement setPropertyMap()");
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800138 return rcValue;
yoshi28bac132014-01-22 11:00:17 -0800139 }
140
141 @Override
142 public <T> T getProperty(String key) {
143 Map<String, Object> map = getPropertyMap();
144 return (T) map.get(key);
145 }
146
147 @Override
148 public Set<String> getPropertyKeys() {
149 Map<String, Object> map = getPropertyMap();
150 return map.keySet();
151 }
152
153 public Map<String, Object> getProperties() {
154 return getPropertyMap();
155 }
156 public void setProperties(Map<String, Object> properties) {
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800157 Map<String, Object> map = getPropertyMap();
yoshi28bac132014-01-22 11:00:17 -0800158 Map<String, Object> oldValueMap = new HashMap<String, Object>(map.size());
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800159 for (Map.Entry<String, Object> property : properties.entrySet()) {
160 String key = property.getKey();
161 if (key == null) {
162 throw ExceptionFactory.propertyKeyCanNotBeNull();
163 }
yoshi28bac132014-01-22 11:00:17 -0800164
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800165 if (key.equals("")) {
166 throw ExceptionFactory.propertyKeyCanNotBeEmpty();
167 }
yoshi28bac132014-01-22 11:00:17 -0800168
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800169 if (key.equals("id")) {
170 throw ExceptionFactory.propertyKeyIdIsReserved();
171 }
yoshi28bac132014-01-22 11:00:17 -0800172
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800173 if (this instanceof RamCloudEdge && key.equals("label")) {
174 throw ExceptionFactory.propertyKeyLabelIsReservedForEdges();
175 }
176 Object value = property.getValue();
177 if (value == null) {
178 throw ExceptionFactory.propertyValueCanNotBeNull();
179 }
yoshi28bac132014-01-22 11:00:17 -0800180
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800181 oldValueMap.put(key, map.put(key, value));
yoshi28bac132014-01-22 11:00:17 -0800182
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800183 }
184 byte[] rcValue = convertVertexPropertyMapToRcBytes(map);
185
186 if (rcValue.length != 0) {
187 if (!writeWithRules(rcValue)) {
188 log.debug("getSetProperties cond. write failure RETRYING 1");
189 for (int i = 0; i < graph.CONDITIONALWRITE_RETRY_MAX ; i++){
190 map = getPropertyMap();
191 oldValueMap = new HashMap<String, Object>(map.size());
192 for (Map.Entry<String, Object> property : properties.entrySet()) {
193 String key = property.getKey();
194 Object value = property.getValue();
195 oldValueMap.put(key, map.put(key, value));
196 }
197
198 rcValue = convertVertexPropertyMapToRcBytes(map);
199 if (rcValue.length != 0) {
200 if (writeWithRules(rcValue)) {
201 break;
202 } else {
203 log.debug("getSetProperties cond. write failure RETRYING {}", i+1);
204 if (i + 1 == graph.CONDITIONALWRITE_RETRY_MAX) {
205 log.error("setProperties cond. write failure Gaveup RETRYING");
206 }
207 }
208 }
209 }
210 }
211 }
yoshi28bac132014-01-22 11:00:17 -0800212
213 // TODO use multi-write
214 for (Map.Entry<String, Object> oldProperty : oldValueMap.entrySet()) {
215 String key = oldProperty.getKey();
216 Object oldValue = oldProperty.getValue();
217 Object value = map.get(key);
218 if (this instanceof RamCloudVertex) {
219 RamCloudKeyIndex keyIndex = new RamCloudKeyIndex(graph.kidxVertTableId, key, value, graph, Vertex.class);
220 keyIndex.autoUpdate(key, value, oldValue, this);
221 } else {
222 RamCloudKeyIndex keyIndex = new RamCloudKeyIndex(graph.kidxVertTableId, key, value, graph, Edge.class);
223 keyIndex.autoUpdate(key, value, oldValue, this);
224 }
225 }
226 }
227
228 @Override
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800229 public void setProperty(String propKey, Object propValue) {
230 Object oldValue = null;
231 if (propValue == null) {
yoshi28bac132014-01-22 11:00:17 -0800232 throw ExceptionFactory.propertyValueCanNotBeNull();
233 }
234
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800235 if (propKey == null) {
yoshi28bac132014-01-22 11:00:17 -0800236 throw ExceptionFactory.propertyKeyCanNotBeNull();
237 }
238
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800239 if (propKey.equals("")) {
yoshi28bac132014-01-22 11:00:17 -0800240 throw ExceptionFactory.propertyKeyCanNotBeEmpty();
241 }
242
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800243 if (propKey.equals("id")) {
yoshi28bac132014-01-22 11:00:17 -0800244 throw ExceptionFactory.propertyKeyIdIsReserved();
245 }
246
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800247 if (this instanceof RamCloudEdge && propKey.equals("label")) {
yoshi28bac132014-01-22 11:00:17 -0800248 throw ExceptionFactory.propertyKeyLabelIsReservedForEdges();
249 }
250
251 long startTime = 0;
252 if (graph.measureBPTimeProp == 1) {
253 startTime = System.nanoTime();
254 }
255
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800256 for (int i = 0; i < graph.CONDITIONALWRITE_RETRY_MAX; i++) {
257 Map<String, Object> map = getPropertyMap();
258 oldValue = map.put(propKey, propValue);
259
260 byte[] rcValue = convertVertexPropertyMapToRcBytes(map);
261
262 if (rcValue.length != 0) {
263 if (writeWithRules(rcValue)) {
264 break;
265 } else {
266 log.debug("setProperty(String {}, Object {}) cond. write failure RETRYING {}", propKey, propValue, i+1);
267 if (i + 1 == graph.CONDITIONALWRITE_RETRY_MAX) {
268 log.error("setProperty(String {}, Object {}) cond. write failure Gaveup RETRYING", propKey, propValue);
269 }
270 }
271 }
272 }
yoshi28bac132014-01-22 11:00:17 -0800273
274 boolean ret = false;
275 if (this instanceof RamCloudVertex) {
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800276 RamCloudKeyIndex keyIndex = new RamCloudKeyIndex(graph.kidxVertTableId, propKey, propValue, graph, Vertex.class);
277 ret = keyIndex.autoUpdate(propKey, propValue, oldValue, this);
yoshi28bac132014-01-22 11:00:17 -0800278 } else {
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800279 RamCloudKeyIndex keyIndex = new RamCloudKeyIndex(graph.kidxVertTableId, propKey, propValue, graph, Edge.class);
280 keyIndex.autoUpdate(propKey, propValue, oldValue, this);
yoshi28bac132014-01-22 11:00:17 -0800281 }
282
283 if (graph.measureBPTimeProp == 1) {
284 long endTime = System.nanoTime();
285 if (ret) {
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800286 log.error("Performance vertex setProperty(key {}) which is total time {}", propKey, endTime - startTime);
yoshi28bac132014-01-22 11:00:17 -0800287 } else {
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800288 log.error("Performance vertex setProperty(key {}) does not time {}", propKey, endTime - startTime);
yoshi28bac132014-01-22 11:00:17 -0800289 }
290 }
291
292 }
293
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800294 protected boolean writeWithRules(byte[] rcValue) {
295 return RamCloudWrite.writeWithRules(this.rcPropTableId, this.rcPropTableKey, rcValue, this.propVersion, this.graph, RamCloudWrite.PerfMonEnum.WRITE);
296 }
297
yoshi28bac132014-01-22 11:00:17 -0800298 @Override
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800299 public <T> T removeProperty(String propKey) {
300 T retVal = null;
301 for (int i = 0; i < graph.CONDITIONALWRITE_RETRY_MAX; i++) {
302 Map<String, Object> map = getPropertyMap();
303 retVal = (T) map.remove(propKey);
304 byte[] rcValue = convertVertexPropertyMapToRcBytes(map);
305
306 if (rcValue.length != 0) {
307 if (writeWithRules(rcValue)) {
308 break;
309 } else {
310 log.debug("removeProperty(String {}) cond. write failure RETRYING {}", propKey, i+1);
311 if (i + 1 == graph.CONDITIONALWRITE_RETRY_MAX) {
312 log.error("removeProperty(String {}) cond. write failure Gaveup RETRYING", propKey);
313 }
314 }
315 }
316 }
317
yoshi28bac132014-01-22 11:00:17 -0800318 if (this instanceof RamCloudVertex) {
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800319 RamCloudKeyIndex keyIndex = new RamCloudKeyIndex(graph.kidxVertTableId, propKey, retVal, graph, Vertex.class);
320 keyIndex.autoRemove(propKey, retVal.toString(), this);
yoshi28bac132014-01-22 11:00:17 -0800321 } else {
Yoshi Muroi815c7f92014-01-30 18:06:16 -0800322 RamCloudKeyIndex keyIndex = new RamCloudKeyIndex(graph.kidxVertTableId, propKey, retVal, graph, Edge.class);
323 keyIndex.autoRemove(propKey, retVal.toString(), this);
yoshi28bac132014-01-22 11:00:17 -0800324 }
325
326 return retVal;
327 }
328
329 @Override
330 public void remove() {
331 graph.getRcClient().remove(rcPropTableId, rcPropTableKey);
332 }
333
334 @Override
335 public Object getId() {
336 // TODO Auto-generated method stub
337 return null;
338 }
339
340 @Override
341 public String toString() {
342 return "RamCloudElement [rcPropTableKey=" + Arrays.toString(rcPropTableKey)
343 + ", rcPropTableId=" + rcPropTableId + "]";
344 }
345}