Yuta HIGUCHI

Add sample accessing database service to Foo

Change-Id: I514c57a278dea368448d284eb5bf0d41bb0013e3
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
15 */ 15 */
16 package org.onlab.onos.foo; 16 package org.onlab.onos.foo;
17 17
18 +import java.nio.ByteBuffer;
19 +import java.util.concurrent.ScheduledExecutorService;
20 +
18 import org.apache.felix.scr.annotations.Activate; 21 import org.apache.felix.scr.annotations.Activate;
19 import org.apache.felix.scr.annotations.Component; 22 import org.apache.felix.scr.annotations.Component;
20 import org.apache.felix.scr.annotations.Deactivate; 23 import org.apache.felix.scr.annotations.Deactivate;
...@@ -33,9 +36,20 @@ import org.onlab.onos.net.device.DeviceService; ...@@ -33,9 +36,20 @@ import org.onlab.onos.net.device.DeviceService;
33 import org.onlab.onos.net.intent.IntentEvent; 36 import org.onlab.onos.net.intent.IntentEvent;
34 import org.onlab.onos.net.intent.IntentListener; 37 import org.onlab.onos.net.intent.IntentListener;
35 import org.onlab.onos.net.intent.IntentService; 38 import org.onlab.onos.net.intent.IntentService;
39 +import org.onlab.onos.store.service.DatabaseAdminService;
40 +import org.onlab.onos.store.service.DatabaseException;
41 +import org.onlab.onos.store.service.DatabaseService;
42 +import org.onlab.onos.store.service.OptionalResult;
43 +import org.onlab.onos.store.service.PreconditionFailedException;
44 +import org.onlab.onos.store.service.ReadRequest;
45 +import org.onlab.onos.store.service.ReadResult;
46 +import org.onlab.onos.store.service.WriteRequest;
47 +import org.onlab.onos.store.service.WriteResult;
36 import org.slf4j.Logger; 48 import org.slf4j.Logger;
37 49
50 +import static org.onlab.util.Tools.namedThreads;
38 import static org.slf4j.LoggerFactory.getLogger; 51 import static org.slf4j.LoggerFactory.getLogger;
52 +import static java.util.concurrent.Executors.newScheduledThreadPool;
39 53
40 /** 54 /**
41 * Playground app component. 55 * Playground app component.
...@@ -57,22 +71,42 @@ public class FooComponent { ...@@ -57,22 +71,42 @@ public class FooComponent {
57 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
58 protected MastershipService mastershipService; 72 protected MastershipService mastershipService;
59 73
74 + @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
75 + protected DatabaseAdminService dbAdminService;
76 +
77 + @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
78 + protected DatabaseService dbService;
79 +
60 private final ClusterEventListener clusterListener = new InnerClusterListener(); 80 private final ClusterEventListener clusterListener = new InnerClusterListener();
61 private final DeviceListener deviceListener = new InnerDeviceListener(); 81 private final DeviceListener deviceListener = new InnerDeviceListener();
62 private final IntentListener intentListener = new InnerIntentListener(); 82 private final IntentListener intentListener = new InnerIntentListener();
63 private final MastershipListener mastershipListener = new InnerMastershipListener(); 83 private final MastershipListener mastershipListener = new InnerMastershipListener();
64 84
85 + private ScheduledExecutorService executor;
86 +
65 @Activate 87 @Activate
66 public void activate() { 88 public void activate() {
89 + executor = newScheduledThreadPool(4, namedThreads("foo-executor-%d"));
90 +
67 clusterService.addListener(clusterListener); 91 clusterService.addListener(clusterListener);
68 deviceService.addListener(deviceListener); 92 deviceService.addListener(deviceListener);
69 intentService.addListener(intentListener); 93 intentService.addListener(intentListener);
70 mastershipService.addListener(mastershipListener); 94 mastershipService.addListener(mastershipListener);
95 +
96 + if (dbService == null || dbAdminService == null) {
97 + log.info("Couldn't find DB service");
98 + } else {
99 + log.info("Found DB service");
100 +// longIncrementor();
101 +// executor.scheduleAtFixedRate(new LongIncrementor(), 1, 10, TimeUnit.SECONDS);
102 +// executor.scheduleAtFixedRate(new LongIncrementor(), 1, 10, TimeUnit.SECONDS);
103 + }
71 log.info("Started"); 104 log.info("Started");
72 } 105 }
73 106
74 @Deactivate 107 @Deactivate
75 public void deactivate() { 108 public void deactivate() {
109 + executor.shutdown();
76 clusterService.removeListener(clusterListener); 110 clusterService.removeListener(clusterListener);
77 deviceService.removeListener(deviceListener); 111 deviceService.removeListener(deviceListener);
78 intentService.removeListener(intentListener); 112 intentService.removeListener(intentListener);
...@@ -122,6 +156,65 @@ public class FooComponent { ...@@ -122,6 +156,65 @@ public class FooComponent {
122 } 156 }
123 } 157 }
124 } 158 }
159 +
160 + private void longIncrementor() {
161 + try {
162 + final String someTable = "admin";
163 + final String someKey = "long";
164 +
165 + dbAdminService.createTable(someTable);
166 +
167 + ReadResult read = dbService.read(ReadRequest.get(someTable, someKey));
168 + if (!read.valueExists()) {
169 + ByteBuffer zero = ByteBuffer.allocate(Long.BYTES).putLong(0);
170 + try {
171 + dbService.write(WriteRequest
172 + .putIfAbsent(someTable,
173 + someKey,
174 + zero.array()));
175 + log.info("Wrote initial value");
176 + read = dbService.read(ReadRequest.get(someTable, someKey));
177 + } catch (PreconditionFailedException e) {
178 + log.info("Concurrent write detected.", e);
179 +
180 + // concurrent write detected, read and fall through
181 + read = dbService.read(ReadRequest.get(someTable, someKey));
182 + if (!read.valueExists()) {
183 + log.error("Shouldn't reach here");
184 + }
185 + }
186 + }
187 + int retry = 5;
188 + do {
189 + ByteBuffer prev = ByteBuffer.wrap(read.value().value());
190 + long next = prev.getLong() + 1;
191 + byte[] newValue = ByteBuffer.allocate(Long.BYTES).putLong(next).array();
192 + OptionalResult<WriteResult, DatabaseException> result
193 + = dbService.writeNothrow(WriteRequest
194 + .putIfVersionMatches(someTable,
195 + someKey,
196 + newValue,
197 + read.value().version()));
198 + if (result.hasValidResult()) {
199 + log.info("Write success {} -> {}", result.get().previousValue(), next);
200 + break;
201 + } else {
202 + log.info("Write failed trying to write{}", next);
203 + }
204 + } while(retry-- > 0);
205 + } catch (Exception e) {
206 + log.error("Exception thrown", e);
207 + }
208 + }
209 +
210 + private final class LongIncrementor implements Runnable {
211 +
212 + @Override
213 + public void run() {
214 + longIncrementor();
215 + }
216 +
217 + }
125 } 218 }
126 219
127 220
......
...@@ -24,13 +24,24 @@ public interface DatabaseService { ...@@ -24,13 +24,24 @@ public interface DatabaseService {
24 */ 24 */
25 List<OptionalResult<ReadResult, DatabaseException>> batchRead(List<ReadRequest> batch); 25 List<OptionalResult<ReadResult, DatabaseException>> batchRead(List<ReadRequest> batch);
26 26
27 + // FIXME Give me a better name
27 /** 28 /**
28 * Performs a write operation on the database. 29 * Performs a write operation on the database.
29 * @param request write request 30 * @param request write request
30 * @return write result. 31 * @return write result.
31 * @throws DatabaseException if there is failure in execution write. 32 * @throws DatabaseException if there is failure in execution write.
32 */ 33 */
33 - WriteResult write(WriteRequest request); 34 + OptionalResult<WriteResult, DatabaseException> writeNothrow(WriteRequest request);
35 +
36 + /**
37 + * Performs a write operation on the database.
38 + * @param request write request
39 + * @return write result.
40 + * @throws OptimisticLockException FIXME define conditional failure
41 + * @throws PreconditionFailedException FIXME define conditional failure
42 + * @throws DatabaseException if there is failure in execution write.
43 + */
44 + WriteResult write(WriteRequest request)/* throws OptimisticLockException, PreconditionFailedException*/;
34 45
35 /** 46 /**
36 * Performs a batch write operation on the database. 47 * Performs a batch write operation on the database.
......
1 package org.onlab.onos.store.service; 1 package org.onlab.onos.store.service;
2 2
3 -
4 /** 3 /**
5 * Exception that indicates a precondition failure. 4 * Exception that indicates a precondition failure.
6 * Scenarios that can cause this exception: 5 * Scenarios that can cause this exception:
......
...@@ -35,6 +35,15 @@ public class ReadResult { ...@@ -35,6 +35,15 @@ public class ReadResult {
35 } 35 }
36 36
37 /** 37 /**
38 + * Returns true if database table contained value for the key.
39 + *
40 + * @return true if database table contained value for the key
41 + */
42 + public boolean valueExists() {
43 + return value != null;
44 + }
45 +
46 + /**
38 * Returns value associated with the key. 47 * Returns value associated with the key.
39 * @return non-null value if the table contains one, null otherwise. 48 * @return non-null value if the table contains one, null otherwise.
40 */ 49 */
......
1 package org.onlab.onos.store.service; 1 package org.onlab.onos.store.service;
2 2
3 import static com.google.common.base.Preconditions.checkArgument; 3 import static com.google.common.base.Preconditions.checkArgument;
4 +import static com.google.common.base.Preconditions.checkNotNull;
5 +import static org.onlab.onos.store.service.WriteRequest.Type.*;
4 6
5 import java.util.Objects; 7 import java.util.Objects;
6 8
...@@ -11,8 +13,13 @@ import com.google.common.base.MoreObjects; ...@@ -11,8 +13,13 @@ import com.google.common.base.MoreObjects;
11 */ 13 */
12 public class WriteRequest { 14 public class WriteRequest {
13 15
16 + public static final int ANY_VERSION = -1;
17 +
14 private final String tableName; 18 private final String tableName;
15 private final String key; 19 private final String key;
20 +
21 + private final Type type;
22 +
16 private final byte[] newValue; 23 private final byte[] newValue;
17 private final long previousVersion; 24 private final long previousVersion;
18 private final byte[] oldValue; 25 private final byte[] oldValue;
...@@ -23,22 +30,22 @@ public class WriteRequest { ...@@ -23,22 +30,22 @@ public class WriteRequest {
23 * 30 *
24 * @param tableName name of the table 31 * @param tableName name of the table
25 * @param key key in the table 32 * @param key key in the table
26 - * @param newValue value to write 33 + * @param newValue value to write, must not be null
27 * @return WriteRequest 34 * @return WriteRequest
28 */ 35 */
29 public static WriteRequest put(String tableName, String key, 36 public static WriteRequest put(String tableName, String key,
30 byte[] newValue) { 37 byte[] newValue) {
31 - return new WriteRequest(tableName, key, newValue, -1, null); 38 + return new WriteRequest(PUT, tableName, key,
39 + checkNotNull(newValue), ANY_VERSION, null);
32 } 40 }
33 41
34 - // FIXME: Is there a special version value to realize putIfAbsent?
35 /** 42 /**
36 * Creates a write request, which will 43 * Creates a write request, which will
37 * put the specified value to the table if the previous version matches. 44 * put the specified value to the table if the previous version matches.
38 * 45 *
39 * @param tableName name of the table 46 * @param tableName name of the table
40 * @param key key in the table 47 * @param key key in the table
41 - * @param newValue value to write 48 + * @param newValue value to write, must not be null
42 * @param previousVersion previous version expected 49 * @param previousVersion previous version expected
43 * @return WriteRequest 50 * @return WriteRequest
44 */ 51 */
...@@ -46,37 +53,107 @@ public class WriteRequest { ...@@ -46,37 +53,107 @@ public class WriteRequest {
46 byte[] newValue, 53 byte[] newValue,
47 long previousVersion) { 54 long previousVersion) {
48 checkArgument(previousVersion >= 0); 55 checkArgument(previousVersion >= 0);
49 - return new WriteRequest(tableName, key, newValue, previousVersion, null); 56 + return new WriteRequest(PUT_IF_VERSION, tableName, key,
57 + checkNotNull(newValue), previousVersion, null);
50 } 58 }
51 59
52 - // FIXME: What is the behavior of oldValue=null? putIfAbsent?
53 /** 60 /**
54 * Creates a write request, which will 61 * Creates a write request, which will
55 * put the specified value to the table if the previous value matches. 62 * put the specified value to the table if the previous value matches.
56 * 63 *
57 * @param tableName name of the table 64 * @param tableName name of the table
58 * @param key key in the table 65 * @param key key in the table
59 - * @param newValue value to write 66 + * @param newValue value to write, must not be null
60 - * @param oldValue previous value expected 67 + * @param oldValue previous value expected, must not be null
61 * @return WriteRequest 68 * @return WriteRequest
62 */ 69 */
63 public static WriteRequest putIfValueMatches(String tableName, String key, 70 public static WriteRequest putIfValueMatches(String tableName, String key,
64 byte[] newValue, 71 byte[] newValue,
65 byte[] oldValue) { 72 byte[] oldValue) {
66 - return new WriteRequest(tableName, key, newValue, -1, oldValue); 73 + return new WriteRequest(PUT_IF_VALUE, tableName, key,
74 + checkNotNull(newValue), ANY_VERSION,
75 + checkNotNull(oldValue));
76 + }
77 +
78 + /**
79 + * Creates a write request, which will
80 + * put the specified value to the table if the previous value does not exist.
81 + *
82 + * @param tableName name of the table
83 + * @param key key in the table
84 + * @param newValue value to write, must not be null
85 + * @return WriteRequest
86 + */
87 + public static WriteRequest putIfAbsent(String tableName, String key,
88 + byte[] newValue) {
89 + return new WriteRequest(PUT_IF_ABSENT, tableName, key,
90 + checkNotNull(newValue), ANY_VERSION, null);
67 } 91 }
68 92
69 - // FIXME: How do we remove value? newValue=null? 93 + /**
94 + * Creates a write request, which will
95 + * remove the specified entry from the table regardless of the previous value.
96 + *
97 + * @param tableName name of the table
98 + * @param key key in the table
99 + * @return WriteRequest
100 + */
101 + public static WriteRequest remove(String tableName, String key) {
102 + return new WriteRequest(REMOVE, tableName, key,
103 + null, ANY_VERSION, null);
104 + }
105 +
106 + /**
107 + * Creates a write request, which will
108 + * remove the specified entry from the table if the previous version matches.
109 + *
110 + * @param tableName name of the table
111 + * @param key key in the table
112 + * @param previousVersion previous version expected
113 + * @return WriteRequest
114 + */
115 + public static WriteRequest remove(String tableName, String key,
116 + long previousVersion) {
117 + return new WriteRequest(REMOVE_IF_VALUE, tableName, key,
118 + null, previousVersion, null);
119 + }
120 +
121 + /**
122 + * Creates a write request, which will
123 + * remove the specified entry from the table if the previous value matches.
124 + *
125 + * @param tableName name of the table
126 + * @param key key in the table
127 + * @param oldValue previous value expected, must not be null
128 + * @return WriteRequest
129 + */
130 + public static WriteRequest remove(String tableName, String key,
131 + byte[] oldValue) {
132 + return new WriteRequest(Type.REMOVE_IF_VALUE, tableName, key,
133 + null, ANY_VERSION, checkNotNull(oldValue));
134 + }
135 +
136 + public enum Type {
137 + PUT,
138 + PUT_IF_VERSION,
139 + PUT_IF_VALUE,
140 + PUT_IF_ABSENT,
141 + REMOVE,
142 + REMOVE_IF_VERSION,
143 + REMOVE_IF_VALUE,
144 + }
70 145
71 // hidden constructor 146 // hidden constructor
72 - protected WriteRequest(String tableName, String key, byte[] newValue, long previousVersion, byte[] oldValue) { 147 + protected WriteRequest(Type type, String tableName, String key,
148 + byte[] newValue,
149 + long previousVersion, byte[] oldValue) {
73 150
74 - checkArgument(tableName != null); 151 + checkNotNull(tableName);
75 - checkArgument(key != null); 152 + checkNotNull(key);
76 - checkArgument(newValue != null);
77 153
78 this.tableName = tableName; 154 this.tableName = tableName;
79 this.key = key; 155 this.key = key;
156 + this.type = type;
80 this.newValue = newValue; 157 this.newValue = newValue;
81 this.previousVersion = previousVersion; 158 this.previousVersion = previousVersion;
82 this.oldValue = oldValue; 159 this.oldValue = oldValue;
...@@ -90,6 +167,10 @@ public class WriteRequest { ...@@ -90,6 +167,10 @@ public class WriteRequest {
90 return key; 167 return key;
91 } 168 }
92 169
170 + public WriteRequest.Type type() {
171 + return type;
172 + }
173 +
93 public byte[] newValue() { 174 public byte[] newValue() {
94 return newValue; 175 return newValue;
95 } 176 }
...@@ -105,6 +186,7 @@ public class WriteRequest { ...@@ -105,6 +186,7 @@ public class WriteRequest {
105 @Override 186 @Override
106 public String toString() { 187 public String toString() {
107 return MoreObjects.toStringHelper(getClass()) 188 return MoreObjects.toStringHelper(getClass())
189 + .add("type", type)
108 .add("tableName", tableName) 190 .add("tableName", tableName)
109 .add("key", key) 191 .add("key", key)
110 .add("newValue", newValue) 192 .add("newValue", newValue)
...@@ -113,9 +195,10 @@ public class WriteRequest { ...@@ -113,9 +195,10 @@ public class WriteRequest {
113 .toString(); 195 .toString();
114 } 196 }
115 197
198 + // TODO: revisit hashCode, equals condition
116 @Override 199 @Override
117 public int hashCode() { 200 public int hashCode() {
118 - return Objects.hash(key, tableName, previousVersion); 201 + return Objects.hash(type, key, tableName, previousVersion);
119 } 202 }
120 203
121 @Override 204 @Override
...@@ -130,7 +213,8 @@ public class WriteRequest { ...@@ -130,7 +213,8 @@ public class WriteRequest {
130 return false; 213 return false;
131 } 214 }
132 WriteRequest other = (WriteRequest) obj; 215 WriteRequest other = (WriteRequest) obj;
133 - return Objects.equals(this.key, other.key) && 216 + return Objects.equals(this.type, other.type) &&
217 + Objects.equals(this.key, other.key) &&
134 Objects.equals(this.tableName, other.tableName) && 218 Objects.equals(this.tableName, other.tableName) &&
135 Objects.equals(this.previousVersion, other.previousVersion); 219 Objects.equals(this.previousVersion, other.previousVersion);
136 } 220 }
......
...@@ -105,6 +105,7 @@ public class ClusterMessagingProtocol ...@@ -105,6 +105,7 @@ public class ClusterMessagingProtocol
105 private static final KryoNamespace DATABASE = KryoNamespace.newBuilder() 105 private static final KryoNamespace DATABASE = KryoNamespace.newBuilder()
106 .register(ReadRequest.class) 106 .register(ReadRequest.class)
107 .register(WriteRequest.class) 107 .register(WriteRequest.class)
108 + .register(WriteRequest.Type.class)
108 .register(InternalReadResult.class) 109 .register(InternalReadResult.class)
109 .register(InternalWriteResult.class) 110 .register(InternalWriteResult.class)
110 .register(InternalReadResult.Status.class) 111 .register(InternalReadResult.Status.class)
...@@ -135,6 +136,7 @@ public class ClusterMessagingProtocol ...@@ -135,6 +136,7 @@ public class ClusterMessagingProtocol
135 byte[].class) 136 byte[].class)
136 .build(); 137 .build();
137 138
139 + // serializer used for CopyCat Protocol
138 public static final KryoSerializer SERIALIZER = new KryoSerializer() { 140 public static final KryoSerializer SERIALIZER = new KryoSerializer() {
139 @Override 141 @Override
140 protected void setupKryoPool() { 142 protected void setupKryoPool() {
......
...@@ -2,6 +2,7 @@ package org.onlab.onos.store.service.impl; ...@@ -2,6 +2,7 @@ package org.onlab.onos.store.service.impl;
2 2
3 import static org.slf4j.LoggerFactory.getLogger; 3 import static org.slf4j.LoggerFactory.getLogger;
4 4
5 +import java.io.File;
5 import java.util.ArrayList; 6 import java.util.ArrayList;
6 import java.util.Arrays; 7 import java.util.Arrays;
7 import java.util.List; 8 import java.util.List;
...@@ -57,7 +58,7 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { ...@@ -57,7 +58,7 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService {
57 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 58 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
58 protected DatabaseProtocolService copycatMessagingProtocol; 59 protected DatabaseProtocolService copycatMessagingProtocol;
59 60
60 - public static final String LOG_FILE_PREFIX = "onos-copy-cat-log"; 61 + public static final String LOG_FILE_PREFIX = "/tmp/onos-copy-cat-log";
61 62
62 private Copycat copycat; 63 private Copycat copycat;
63 private DatabaseClient client; 64 private DatabaseClient client;
...@@ -126,9 +127,11 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { ...@@ -126,9 +127,11 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService {
126 127
127 128
128 StateMachine stateMachine = new DatabaseStateMachine(); 129 StateMachine stateMachine = new DatabaseStateMachine();
129 - // FIXME resolve Chronicle + OSGi issue 130 + // Chronicle + OSGi issue
130 //Log consensusLog = new ChronicleLog(LOG_FILE_PREFIX + "_" + thisNode.id()); 131 //Log consensusLog = new ChronicleLog(LOG_FILE_PREFIX + "_" + thisNode.id());
131 - Log consensusLog = new KryoRegisteredInMemoryLog(); 132 + //Log consensusLog = new KryoRegisteredInMemoryLog();
133 + Log consensusLog = new MapDBLog(new File(LOG_FILE_PREFIX + localNode.id()),
134 + ClusterMessagingProtocol.SERIALIZER);
132 135
133 copycat = new Copycat(stateMachine, consensusLog, cluster, copycatMessagingProtocol); 136 copycat = new Copycat(stateMachine, consensusLog, cluster, copycatMessagingProtocol);
134 copycat.start(); 137 copycat.start();
...@@ -187,8 +190,14 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { ...@@ -187,8 +190,14 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService {
187 } 190 }
188 191
189 @Override 192 @Override
190 - public WriteResult write(WriteRequest request) { 193 + public OptionalResult<WriteResult, DatabaseException> writeNothrow(WriteRequest request) {
191 - return batchWrite(Arrays.asList(request)).get(0).get(); 194 + return batchWrite(Arrays.asList(request)).get(0);
195 + }
196 +
197 + @Override
198 + public WriteResult write(WriteRequest request)
199 + throws OptimisticLockException, PreconditionFailedException {
200 + return writeNothrow(request).get();
192 } 201 }
193 202
194 @Override 203 @Override
...@@ -199,13 +208,13 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { ...@@ -199,13 +208,13 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService {
199 if (internalWriteResult.status() == InternalWriteResult.Status.NO_SUCH_TABLE) { 208 if (internalWriteResult.status() == InternalWriteResult.Status.NO_SUCH_TABLE) {
200 writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>( 209 writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>(
201 new NoSuchTableException())); 210 new NoSuchTableException()));
202 - } else if (internalWriteResult.status() == InternalWriteResult.Status.OPTIMISTIC_LOCK_FAILURE) { 211 + } else if (internalWriteResult.status() == InternalWriteResult.Status.PREVIOUS_VERSION_MISMATCH) {
203 writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>( 212 writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>(
204 new OptimisticLockException())); 213 new OptimisticLockException()));
205 } else if (internalWriteResult.status() == InternalWriteResult.Status.PREVIOUS_VALUE_MISMATCH) { 214 } else if (internalWriteResult.status() == InternalWriteResult.Status.PREVIOUS_VALUE_MISMATCH) {
206 // TODO: throw a different exception? 215 // TODO: throw a different exception?
207 writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>( 216 writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>(
208 - new PreconditionFailedException())); 217 + new OptimisticLockException()));
209 } else if (internalWriteResult.status() == InternalWriteResult.Status.ABORTED) { 218 } else if (internalWriteResult.status() == InternalWriteResult.Status.ABORTED) {
210 writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>( 219 writeResults.add(new DatabaseOperationResult<WriteResult, DatabaseException>(
211 new WriteAborted())); 220 new WriteAborted()));
......
...@@ -3,8 +3,10 @@ package org.onlab.onos.store.service.impl; ...@@ -3,8 +3,10 @@ package org.onlab.onos.store.service.impl;
3 import static org.slf4j.LoggerFactory.getLogger; 3 import static org.slf4j.LoggerFactory.getLogger;
4 4
5 import java.util.ArrayList; 5 import java.util.ArrayList;
6 +import java.util.Arrays;
6 import java.util.List; 7 import java.util.List;
7 import java.util.Map; 8 import java.util.Map;
9 +
8 import net.kuujo.copycat.Command; 10 import net.kuujo.copycat.Command;
9 import net.kuujo.copycat.Query; 11 import net.kuujo.copycat.Query;
10 import net.kuujo.copycat.StateMachine; 12 import net.kuujo.copycat.StateMachine;
...@@ -15,6 +17,7 @@ import org.onlab.onos.store.service.ReadResult; ...@@ -15,6 +17,7 @@ import org.onlab.onos.store.service.ReadResult;
15 import org.onlab.onos.store.service.VersionedValue; 17 import org.onlab.onos.store.service.VersionedValue;
16 import org.onlab.onos.store.service.WriteRequest; 18 import org.onlab.onos.store.service.WriteRequest;
17 import org.onlab.onos.store.service.WriteResult; 19 import org.onlab.onos.store.service.WriteResult;
20 +import org.onlab.onos.store.service.impl.InternalWriteResult.Status;
18 import org.onlab.util.KryoNamespace; 21 import org.onlab.util.KryoNamespace;
19 import org.slf4j.Logger; 22 import org.slf4j.Logger;
20 23
...@@ -32,6 +35,7 @@ public class DatabaseStateMachine implements StateMachine { ...@@ -32,6 +35,7 @@ public class DatabaseStateMachine implements StateMachine {
32 35
33 private final Logger log = getLogger(getClass()); 36 private final Logger log = getLogger(getClass());
34 37
38 + // serializer used for snapshot
35 public static final KryoSerializer SERIALIZER = new KryoSerializer() { 39 public static final KryoSerializer SERIALIZER = new KryoSerializer() {
36 @Override 40 @Override
37 protected void setupKryoPool() { 41 protected void setupKryoPool() {
...@@ -87,6 +91,37 @@ public class DatabaseStateMachine implements StateMachine { ...@@ -87,6 +91,37 @@ public class DatabaseStateMachine implements StateMachine {
87 return results; 91 return results;
88 } 92 }
89 93
94 + InternalWriteResult.Status checkIfApplicable(WriteRequest request,
95 + VersionedValue value) {
96 +
97 + switch (request.type()) {
98 + case PUT:
99 + return InternalWriteResult.Status.OK;
100 +
101 + case PUT_IF_ABSENT:
102 + if (value == null) {
103 + return InternalWriteResult.Status.OK;
104 + }
105 + return InternalWriteResult.Status.PREVIOUS_VALUE_MISMATCH;
106 + case PUT_IF_VALUE:
107 + case REMOVE_IF_VALUE:
108 + if (value != null && Arrays.equals(value.value(), request.oldValue())) {
109 + return InternalWriteResult.Status.OK;
110 + }
111 + return InternalWriteResult.Status.PREVIOUS_VALUE_MISMATCH;
112 + case PUT_IF_VERSION:
113 + case REMOVE_IF_VERSION:
114 + if (value != null && request.previousVersion() == value.version()) {
115 + return InternalWriteResult.Status.OK;
116 + }
117 + return InternalWriteResult.Status.PREVIOUS_VERSION_MISMATCH;
118 + case REMOVE:
119 + return InternalWriteResult.Status.OK;
120 + }
121 + log.error("Should never reach here {}", request);
122 + return InternalWriteResult.Status.ABORTED;
123 + }
124 +
90 @Command 125 @Command
91 public List<InternalWriteResult> write(List<WriteRequest> requests) { 126 public List<InternalWriteResult> write(List<WriteRequest> requests) {
92 127
...@@ -100,25 +135,12 @@ public class DatabaseStateMachine implements StateMachine { ...@@ -100,25 +135,12 @@ public class DatabaseStateMachine implements StateMachine {
100 abort = true; 135 abort = true;
101 continue; 136 continue;
102 } 137 }
103 - VersionedValue value = table.get(request.key()); 138 + final VersionedValue value = table.get(request.key());
104 - if (value == null) { 139 + Status result = checkIfApplicable(request, value);
105 - if (request.oldValue() != null) { 140 + validationResults.add(result);
106 - validationResults.add(InternalWriteResult.Status.PREVIOUS_VALUE_MISMATCH); 141 + if (result != Status.OK) {
107 - abort = true;
108 - continue;
109 - } else if (request.previousVersion() >= 0) {
110 - validationResults.add(InternalWriteResult.Status.OPTIMISTIC_LOCK_FAILURE);
111 - abort = true;
112 - continue;
113 - }
114 - }
115 - if (request.previousVersion() >= 0 && value.version() != request.previousVersion()) {
116 - validationResults.add(InternalWriteResult.Status.OPTIMISTIC_LOCK_FAILURE);
117 abort = true; 142 abort = true;
118 - continue;
119 } 143 }
120 -
121 - validationResults.add(InternalWriteResult.Status.OK);
122 } 144 }
123 145
124 List<InternalWriteResult> results = new ArrayList<>(requests.size()); 146 List<InternalWriteResult> results = new ArrayList<>(requests.size());
...@@ -126,6 +148,7 @@ public class DatabaseStateMachine implements StateMachine { ...@@ -126,6 +148,7 @@ public class DatabaseStateMachine implements StateMachine {
126 if (abort) { 148 if (abort) {
127 for (InternalWriteResult.Status validationResult : validationResults) { 149 for (InternalWriteResult.Status validationResult : validationResults) {
128 if (validationResult == InternalWriteResult.Status.OK) { 150 if (validationResult == InternalWriteResult.Status.OK) {
151 + // aborted due to applicability check failure on other request
129 results.add(new InternalWriteResult(InternalWriteResult.Status.ABORTED, null)); 152 results.add(new InternalWriteResult(InternalWriteResult.Status.ABORTED, null));
130 } else { 153 } else {
131 results.add(new InternalWriteResult(validationResult, null)); 154 results.add(new InternalWriteResult(validationResult, null));
...@@ -141,12 +164,31 @@ public class DatabaseStateMachine implements StateMachine { ...@@ -141,12 +164,31 @@ public class DatabaseStateMachine implements StateMachine {
141 // synchronization scope is wrong. 164 // synchronization scope is wrong.
142 // Whole function including applicability check needs to be protected. 165 // Whole function including applicability check needs to be protected.
143 // Confirm copycat's thread safety requirement for StateMachine 166 // Confirm copycat's thread safety requirement for StateMachine
167 + // TODO: If we need isolation, we need to block reads also
144 synchronized (table) { 168 synchronized (table) {
145 - VersionedValue previousValue = 169 + switch (request.type()) {
146 - table.put(request.key(), new VersionedValue(request.newValue(), state.nextVersion())); 170 + case PUT:
147 - results.add(new InternalWriteResult( 171 + case PUT_IF_ABSENT:
148 - InternalWriteResult.Status.OK, 172 + case PUT_IF_VALUE:
149 - new WriteResult(request.tableName(), request.key(), previousValue))); 173 + case PUT_IF_VERSION:
174 + VersionedValue newValue = new VersionedValue(request.newValue(), state.nextVersion());
175 + VersionedValue previousValue = table.put(request.key(), newValue);
176 + WriteResult putResult = new WriteResult(request.tableName(), request.key(), previousValue);
177 + results.add(InternalWriteResult.ok(putResult));
178 + break;
179 +
180 + case REMOVE:
181 + case REMOVE_IF_VALUE:
182 + case REMOVE_IF_VERSION:
183 + VersionedValue removedValue = table.remove(request.key());
184 + WriteResult removeResult = new WriteResult(request.tableName(), request.key(), removedValue);
185 + results.add(InternalWriteResult.ok(removeResult));
186 + break;
187 +
188 + default:
189 + log.error("Invalid WriteRequest type {}", request.type());
190 + break;
191 + }
150 } 192 }
151 } 193 }
152 return results; 194 return results;
......
1 package org.onlab.onos.store.service.impl; 1 package org.onlab.onos.store.service.impl;
2 2
3 +import java.io.Serializable;
4 +
3 import org.onlab.onos.store.service.ReadResult; 5 import org.onlab.onos.store.service.ReadResult;
4 6
5 /** 7 /**
6 * Result of a read operation executed on the DatabaseStateMachine. 8 * Result of a read operation executed on the DatabaseStateMachine.
7 */ 9 */
8 -public class InternalReadResult { 10 +@SuppressWarnings("serial")
11 +public class InternalReadResult implements Serializable {
9 12
10 public enum Status { 13 public enum Status {
11 OK, 14 OK,
......
...@@ -11,13 +11,17 @@ public class InternalWriteResult { ...@@ -11,13 +11,17 @@ public class InternalWriteResult {
11 OK, 11 OK,
12 ABORTED, 12 ABORTED,
13 NO_SUCH_TABLE, 13 NO_SUCH_TABLE,
14 - OPTIMISTIC_LOCK_FAILURE, 14 + PREVIOUS_VERSION_MISMATCH,
15 PREVIOUS_VALUE_MISMATCH 15 PREVIOUS_VALUE_MISMATCH
16 } 16 }
17 17
18 private final Status status; 18 private final Status status;
19 private final WriteResult result; 19 private final WriteResult result;
20 20
21 + public static InternalWriteResult ok(WriteResult result) {
22 + return new InternalWriteResult(Status.OK, result);
23 + }
24 +
21 public InternalWriteResult(Status status, WriteResult result) { 25 public InternalWriteResult(Status status, WriteResult result) {
22 this.status = status; 26 this.status = status;
23 this.result = result; 27 this.result = result;
......