Yuta HIGUCHI
Committed by Yuta Higuchi

DistributedFlowRuleStore: synchronized -> Reader/Writer lock

fix for ONOS-195

Change-Id: I3e15104225878d1616fa790095695400bcc43697
...@@ -60,6 +60,7 @@ public interface FlowRuleStore extends Store<FlowRuleBatchEvent, FlowRuleStoreDe ...@@ -60,6 +60,7 @@ public interface FlowRuleStore extends Store<FlowRuleBatchEvent, FlowRuleStoreDe
60 * Stores a batch of flow rules. 60 * Stores a batch of flow rules.
61 * 61 *
62 * @param batchOperation batch of flow rules. 62 * @param batchOperation batch of flow rules.
63 + * A batch can contain flow rules for a single device only.
63 * @return Future response indicating success/failure of the batch operation 64 * @return Future response indicating success/failure of the batch operation
64 * all the way down to the device. 65 * all the way down to the device.
65 */ 66 */
......
...@@ -36,6 +36,7 @@ import java.util.concurrent.Future; ...@@ -36,6 +36,7 @@ import java.util.concurrent.Future;
36 import java.util.concurrent.TimeUnit; 36 import java.util.concurrent.TimeUnit;
37 import java.util.concurrent.TimeoutException; 37 import java.util.concurrent.TimeoutException;
38 import java.util.concurrent.atomic.AtomicInteger; 38 import java.util.concurrent.atomic.AtomicInteger;
39 +import java.util.concurrent.locks.ReentrantReadWriteLock;
39 import java.util.List; 40 import java.util.List;
40 41
41 import org.apache.felix.scr.annotations.Activate; 42 import org.apache.felix.scr.annotations.Activate;
...@@ -109,7 +110,8 @@ public class DistributedFlowRuleStore ...@@ -109,7 +110,8 @@ public class DistributedFlowRuleStore
109 private final Logger log = getLogger(getClass()); 110 private final Logger log = getLogger(getClass());
110 111
111 // primary data: 112 // primary data:
112 - // read/write needs to be synchronized 113 + // read/write needs to be locked
114 + private final ReentrantReadWriteLock flowEntriesLock = new ReentrantReadWriteLock();
113 // store entries as a pile of rules, no info about device tables 115 // store entries as a pile of rules, no info about device tables
114 private final Multimap<DeviceId, StoredFlowEntry> flowEntries 116 private final Multimap<DeviceId, StoredFlowEntry> flowEntries
115 = ArrayListMultimap.<DeviceId, StoredFlowEntry>create(); 117 = ArrayListMultimap.<DeviceId, StoredFlowEntry>create();
...@@ -186,7 +188,7 @@ public class DistributedFlowRuleStore ...@@ -186,7 +188,7 @@ public class DistributedFlowRuleStore
186 @Override 188 @Override
187 public void handle(ClusterMessage message) { 189 public void handle(ClusterMessage message) {
188 FlowRule rule = SERIALIZER.decode(message.payload()); 190 FlowRule rule = SERIALIZER.decode(message.payload());
189 - log.info("received get flow entry request for {}", rule); 191 + log.debug("received get flow entry request for {}", rule);
190 FlowEntry flowEntry = getFlowEntryInternal(rule); 192 FlowEntry flowEntry = getFlowEntryInternal(rule);
191 try { 193 try {
192 message.respond(SERIALIZER.encode(flowEntry)); 194 message.respond(SERIALIZER.encode(flowEntry));
...@@ -201,7 +203,7 @@ public class DistributedFlowRuleStore ...@@ -201,7 +203,7 @@ public class DistributedFlowRuleStore
201 @Override 203 @Override
202 public void handle(ClusterMessage message) { 204 public void handle(ClusterMessage message) {
203 DeviceId deviceId = SERIALIZER.decode(message.payload()); 205 DeviceId deviceId = SERIALIZER.decode(message.payload());
204 - log.info("Received get flow entries request for {} from {}", deviceId, message.sender()); 206 + log.debug("Received get flow entries request for {} from {}", deviceId, message.sender());
205 Set<FlowEntry> flowEntries = getFlowEntriesInternal(deviceId); 207 Set<FlowEntry> flowEntries = getFlowEntriesInternal(deviceId);
206 try { 208 try {
207 message.respond(SERIALIZER.encode(flowEntries)); 209 message.respond(SERIALIZER.encode(flowEntries));
...@@ -240,21 +242,20 @@ public class DistributedFlowRuleStore ...@@ -240,21 +242,20 @@ public class DistributedFlowRuleStore
240 } 242 }
241 243
242 @Override 244 @Override
243 - public synchronized FlowEntry getFlowEntry(FlowRule rule) { 245 + public FlowEntry getFlowEntry(FlowRule rule) {
244 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId()); 246 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
245 247
246 if (!replicaInfo.master().isPresent()) { 248 if (!replicaInfo.master().isPresent()) {
247 log.warn("No master for {}", rule); 249 log.warn("No master for {}", rule);
248 - // TODO: revisit if this should be returning null. 250 + // TODO: should we try returning from backup?
249 - // FIXME: throw a FlowStoreException 251 + return null;
250 - throw new RuntimeException("No master for " + rule);
251 } 252 }
252 253
253 if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) { 254 if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
254 return getFlowEntryInternal(rule); 255 return getFlowEntryInternal(rule);
255 } 256 }
256 257
257 - log.info("Forwarding getFlowEntry to {}, which is the primary (master) for device {}", 258 + log.debug("Forwarding getFlowEntry to {}, which is the primary (master) for device {}",
258 replicaInfo.master().orNull(), rule.deviceId()); 259 replicaInfo.master().orNull(), rule.deviceId());
259 260
260 ClusterMessage message = new ClusterMessage( 261 ClusterMessage message = new ClusterMessage(
...@@ -271,25 +272,28 @@ public class DistributedFlowRuleStore ...@@ -271,25 +272,28 @@ public class DistributedFlowRuleStore
271 } 272 }
272 } 273 }
273 274
274 - private synchronized StoredFlowEntry getFlowEntryInternal(FlowRule rule) { 275 + private StoredFlowEntry getFlowEntryInternal(FlowRule rule) {
275 - for (StoredFlowEntry f : flowEntries.get(rule.deviceId())) { 276 + flowEntriesLock.readLock().lock();
276 - if (f.equals(rule)) { 277 + try {
277 - return f; 278 + for (StoredFlowEntry f : flowEntries.get(rule.deviceId())) {
279 + if (f.equals(rule)) {
280 + return f;
281 + }
278 } 282 }
283 + } finally {
284 + flowEntriesLock.readLock().unlock();
279 } 285 }
280 return null; 286 return null;
281 } 287 }
282 288
283 @Override 289 @Override
284 - public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) { 290 + public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
285 291
286 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId); 292 ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(deviceId);
287 293
288 if (!replicaInfo.master().isPresent()) { 294 if (!replicaInfo.master().isPresent()) {
289 log.warn("No master for {}", deviceId); 295 log.warn("No master for {}", deviceId);
290 - // TODO: revisit if this should be returning empty collection or throwing exception. 296 + // TODO: should we try returning from backup?
291 - // FIXME: throw a FlowStoreException
292 - //throw new RuntimeException("No master for " + deviceId);
293 return Collections.emptyList(); 297 return Collections.emptyList();
294 } 298 }
295 299
...@@ -297,7 +301,7 @@ public class DistributedFlowRuleStore ...@@ -297,7 +301,7 @@ public class DistributedFlowRuleStore
297 return getFlowEntriesInternal(deviceId); 301 return getFlowEntriesInternal(deviceId);
298 } 302 }
299 303
300 - log.info("Forwarding getFlowEntries to {}, which is the primary (master) for device {}", 304 + log.debug("Forwarding getFlowEntries to {}, which is the primary (master) for device {}",
301 replicaInfo.master().orNull(), deviceId); 305 replicaInfo.master().orNull(), deviceId);
302 306
303 ClusterMessage message = new ClusterMessage( 307 ClusterMessage message = new ClusterMessage(
...@@ -314,12 +318,17 @@ public class DistributedFlowRuleStore ...@@ -314,12 +318,17 @@ public class DistributedFlowRuleStore
314 } 318 }
315 } 319 }
316 320
317 - private synchronized Set<FlowEntry> getFlowEntriesInternal(DeviceId deviceId) { 321 + private Set<FlowEntry> getFlowEntriesInternal(DeviceId deviceId) {
318 - Collection<? extends FlowEntry> rules = flowEntries.get(deviceId); 322 + flowEntriesLock.readLock().lock();
319 - if (rules == null) { 323 + try {
320 - return Collections.emptySet(); 324 + Collection<? extends FlowEntry> rules = flowEntries.get(deviceId);
325 + if (rules == null) {
326 + return Collections.emptySet();
327 + }
328 + return ImmutableSet.copyOf(rules);
329 + } finally {
330 + flowEntriesLock.readLock().unlock();
321 } 331 }
322 - return ImmutableSet.copyOf(rules);
323 } 332 }
324 333
325 @Override 334 @Override
...@@ -327,7 +336,6 @@ public class DistributedFlowRuleStore ...@@ -327,7 +336,6 @@ public class DistributedFlowRuleStore
327 storeBatch(new FlowRuleBatchOperation(Arrays.asList(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule)))); 336 storeBatch(new FlowRuleBatchOperation(Arrays.asList(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule))));
328 } 337 }
329 338
330 - // FIXME document that all of the FlowEntries must be about same device
331 @Override 339 @Override
332 public Future<CompletedBatchOperation> storeBatch(FlowRuleBatchOperation operation) { 340 public Future<CompletedBatchOperation> storeBatch(FlowRuleBatchOperation operation) {
333 341
...@@ -351,7 +359,7 @@ public class DistributedFlowRuleStore ...@@ -351,7 +359,7 @@ public class DistributedFlowRuleStore
351 return storeBatchInternal(operation); 359 return storeBatchInternal(operation);
352 } 360 }
353 361
354 - log.info("Forwarding storeBatch to {}, which is the primary (master) for device {}", 362 + log.debug("Forwarding storeBatch to {}, which is the primary (master) for device {}",
355 replicaInfo.master().orNull(), deviceId); 363 replicaInfo.master().orNull(), deviceId);
356 364
357 ClusterMessage message = new ClusterMessage( 365 ClusterMessage message = new ClusterMessage(
...@@ -368,41 +376,46 @@ public class DistributedFlowRuleStore ...@@ -368,41 +376,46 @@ public class DistributedFlowRuleStore
368 } 376 }
369 } 377 }
370 378
371 - private synchronized ListenableFuture<CompletedBatchOperation> 379 + private ListenableFuture<CompletedBatchOperation>
372 storeBatchInternal(FlowRuleBatchOperation operation) { 380 storeBatchInternal(FlowRuleBatchOperation operation) {
373 381
374 final List<StoredFlowEntry> toRemove = new ArrayList<>(); 382 final List<StoredFlowEntry> toRemove = new ArrayList<>();
375 final List<StoredFlowEntry> toAdd = new ArrayList<>(); 383 final List<StoredFlowEntry> toAdd = new ArrayList<>();
376 DeviceId did = null; 384 DeviceId did = null;
377 385
378 - for (FlowRuleBatchEntry batchEntry : operation.getOperations()) { 386 +
379 - FlowRule flowRule = batchEntry.getTarget(); 387 + flowEntriesLock.writeLock().lock();
380 - FlowRuleOperation op = batchEntry.getOperator(); 388 + try {
381 - if (did == null) { 389 + for (FlowRuleBatchEntry batchEntry : operation.getOperations()) {
382 - did = flowRule.deviceId(); 390 + FlowRule flowRule = batchEntry.getTarget();
383 - } 391 + FlowRuleOperation op = batchEntry.getOperator();
384 - if (op.equals(FlowRuleOperation.REMOVE)) { 392 + if (did == null) {
385 - StoredFlowEntry entry = getFlowEntryInternal(flowRule); 393 + did = flowRule.deviceId();
386 - if (entry != null) {
387 - entry.setState(FlowEntryState.PENDING_REMOVE);
388 - toRemove.add(entry);
389 } 394 }
390 - } else if (op.equals(FlowRuleOperation.ADD)) { 395 + if (op.equals(FlowRuleOperation.REMOVE)) {
391 - StoredFlowEntry flowEntry = new DefaultFlowEntry(flowRule); 396 + StoredFlowEntry entry = getFlowEntryInternal(flowRule);
392 - DeviceId deviceId = flowRule.deviceId(); 397 + if (entry != null) {
393 - if (!flowEntries.containsEntry(deviceId, flowEntry)) { 398 + entry.setState(FlowEntryState.PENDING_REMOVE);
394 - flowEntries.put(deviceId, flowEntry); 399 + toRemove.add(entry);
395 - toAdd.add(flowEntry); 400 + }
401 + } else if (op.equals(FlowRuleOperation.ADD)) {
402 + StoredFlowEntry flowEntry = new DefaultFlowEntry(flowRule);
403 + DeviceId deviceId = flowRule.deviceId();
404 + if (!flowEntries.containsEntry(deviceId, flowEntry)) {
405 + flowEntries.put(deviceId, flowEntry);
406 + toAdd.add(flowEntry);
407 + }
396 } 408 }
397 } 409 }
398 - } 410 + if (toAdd.isEmpty() && toRemove.isEmpty()) {
399 - if (toAdd.isEmpty() && toRemove.isEmpty()) { 411 + return Futures.immediateFuture(new CompletedBatchOperation(true, Collections.<FlowRule>emptySet()));
400 - return Futures.immediateFuture(new CompletedBatchOperation(true, Collections.<FlowRule>emptySet())); 412 + }
401 - }
402 413
403 - // create remote backup copies 414 + // create remote backup copies
404 - final DeviceId deviceId = did; 415 + updateBackup(did, toAdd, toRemove);
405 - updateBackup(deviceId, toAdd, toRemove); 416 + } finally {
417 + flowEntriesLock.writeLock().unlock();
418 + }
406 419
407 SettableFuture<CompletedBatchOperation> r = SettableFuture.create(); 420 SettableFuture<CompletedBatchOperation> r = SettableFuture.create();
408 final int batchId = localBatchIdGen.incrementAndGet(); 421 final int batchId = localBatchIdGen.incrementAndGet();
...@@ -451,27 +464,32 @@ public class DistributedFlowRuleStore ...@@ -451,27 +464,32 @@ public class DistributedFlowRuleStore
451 return null; 464 return null;
452 } 465 }
453 466
454 - private synchronized FlowRuleEvent addOrUpdateFlowRuleInternal(FlowEntry rule) { 467 + private FlowRuleEvent addOrUpdateFlowRuleInternal(FlowEntry rule) {
455 final DeviceId did = rule.deviceId(); 468 final DeviceId did = rule.deviceId();
456 469
457 - // check if this new rule is an update to an existing entry 470 + flowEntriesLock.writeLock().lock();
458 - StoredFlowEntry stored = getFlowEntryInternal(rule); 471 + try {
459 - if (stored != null) { 472 + // check if this new rule is an update to an existing entry
460 - stored.setBytes(rule.bytes()); 473 + StoredFlowEntry stored = getFlowEntryInternal(rule);
461 - stored.setLife(rule.life()); 474 + if (stored != null) {
462 - stored.setPackets(rule.packets()); 475 + stored.setBytes(rule.bytes());
463 - if (stored.state() == FlowEntryState.PENDING_ADD) { 476 + stored.setLife(rule.life());
464 - stored.setState(FlowEntryState.ADDED); 477 + stored.setPackets(rule.packets());
465 - // update backup. 478 + if (stored.state() == FlowEntryState.PENDING_ADD) {
466 - updateBackup(did, Arrays.asList(stored)); 479 + stored.setState(FlowEntryState.ADDED);
467 - return new FlowRuleEvent(Type.RULE_ADDED, rule); 480 + // update backup.
481 + updateBackup(did, Arrays.asList(stored));
482 + return new FlowRuleEvent(Type.RULE_ADDED, rule);
483 + }
484 + return new FlowRuleEvent(Type.RULE_UPDATED, rule);
468 } 485 }
469 - return new FlowRuleEvent(Type.RULE_UPDATED, rule);
470 - }
471 486
472 - // TODO: Confirm if this behavior is correct. See SimpleFlowRuleStore 487 + // TODO: Confirm if this behavior is correct. See SimpleFlowRuleStore
473 - // TODO: also update backup. 488 + // TODO: also update backup.
474 - flowEntries.put(did, new DefaultFlowEntry(rule)); 489 + flowEntries.put(did, new DefaultFlowEntry(rule));
490 + } finally {
491 + flowEntriesLock.writeLock().unlock();
492 + }
475 return null; 493 return null;
476 494
477 } 495 }
...@@ -491,15 +509,20 @@ public class DistributedFlowRuleStore ...@@ -491,15 +509,20 @@ public class DistributedFlowRuleStore
491 return null; 509 return null;
492 } 510 }
493 511
494 - private synchronized FlowRuleEvent removeFlowRuleInternal(FlowEntry rule) { 512 + private FlowRuleEvent removeFlowRuleInternal(FlowEntry rule) {
495 final DeviceId deviceId = rule.deviceId(); 513 final DeviceId deviceId = rule.deviceId();
496 - // This is where one could mark a rule as removed and still keep it in the store. 514 + flowEntriesLock.writeLock().lock();
497 - final boolean removed = flowEntries.remove(deviceId, rule); 515 + try {
498 - updateBackup(deviceId, Collections.<StoredFlowEntry>emptyList(), Arrays.asList(rule)); 516 + // This is where one could mark a rule as removed and still keep it in the store.
499 - if (removed) { 517 + final boolean removed = flowEntries.remove(deviceId, rule);
500 - return new FlowRuleEvent(RULE_REMOVED, rule); 518 + updateBackup(deviceId, Collections.<StoredFlowEntry>emptyList(), Arrays.asList(rule));
501 - } else { 519 + if (removed) {
502 - return null; 520 + return new FlowRuleEvent(RULE_REMOVED, rule);
521 + } else {
522 + return null;
523 + }
524 + } finally {
525 + flowEntriesLock.writeLock().unlock();
503 } 526 }
504 } 527 }
505 528
...@@ -515,9 +538,9 @@ public class DistributedFlowRuleStore ...@@ -515,9 +538,9 @@ public class DistributedFlowRuleStore
515 notifyDelegate(event); 538 notifyDelegate(event);
516 } 539 }
517 540
518 - private synchronized void loadFromBackup(final DeviceId did) { 541 + private void loadFromBackup(final DeviceId did) {
519 - // should relax synchronized condition
520 542
543 + flowEntriesLock.writeLock().lock();
521 try { 544 try {
522 log.info("Loading FlowRules for {} from backups", did); 545 log.info("Loading FlowRules for {} from backups", did);
523 SMap<FlowId, ImmutableList<StoredFlowEntry>> backupFlowTable = smaps.get(did); 546 SMap<FlowId, ImmutableList<StoredFlowEntry>> backupFlowTable = smaps.get(did);
...@@ -534,11 +557,19 @@ public class DistributedFlowRuleStore ...@@ -534,11 +557,19 @@ public class DistributedFlowRuleStore
534 } 557 }
535 } catch (ExecutionException e) { 558 } catch (ExecutionException e) {
536 log.error("Failed to load backup flowtable for {}", did, e); 559 log.error("Failed to load backup flowtable for {}", did, e);
560 + } finally {
561 + flowEntriesLock.writeLock().unlock();
537 } 562 }
538 } 563 }
539 564
540 - private synchronized void removeFromPrimary(final DeviceId did) { 565 + private void removeFromPrimary(final DeviceId did) {
541 - Collection<StoredFlowEntry> removed = flowEntries.removeAll(did); 566 + Collection<StoredFlowEntry> removed = null;
567 + flowEntriesLock.writeLock().lock();
568 + try {
569 + removed = flowEntries.removeAll(did);
570 + } finally {
571 + flowEntriesLock.writeLock().unlock();
572 + }
542 log.debug("removedFromPrimary {}", removed); 573 log.debug("removedFromPrimary {}", removed);
543 } 574 }
544 575
......