Bugfixes for DistributedLockManager functionality
Added a method called broadcastIncludeSelf to ClusterCommunicationService. Cosmetic improvements: added toString methods Change-Id: I1d58720c29e6f8642f950670c3a6d95a7019a491
Showing
17 changed files
with
179 additions
and
61 deletions
| ... | @@ -21,7 +21,9 @@ import static org.slf4j.LoggerFactory.getLogger; | ... | @@ -21,7 +21,9 @@ import static org.slf4j.LoggerFactory.getLogger; |
| 21 | 21 | ||
| 22 | import java.nio.ByteBuffer; | 22 | import java.nio.ByteBuffer; |
| 23 | import java.util.concurrent.ScheduledExecutorService; | 23 | import java.util.concurrent.ScheduledExecutorService; |
| 24 | -import java.util.concurrent.TimeUnit; | 24 | +//import java.util.concurrent.TimeUnit; |
| 25 | + | ||
| 26 | + | ||
| 25 | 27 | ||
| 26 | import org.apache.felix.scr.annotations.Activate; | 28 | import org.apache.felix.scr.annotations.Activate; |
| 27 | import org.apache.felix.scr.annotations.Component; | 29 | import org.apache.felix.scr.annotations.Component; |
| ... | @@ -43,6 +45,8 @@ import org.onlab.onos.net.intent.IntentListener; | ... | @@ -43,6 +45,8 @@ import org.onlab.onos.net.intent.IntentListener; |
| 43 | import org.onlab.onos.net.intent.IntentService; | 45 | import org.onlab.onos.net.intent.IntentService; |
| 44 | import org.onlab.onos.store.service.DatabaseAdminService; | 46 | import org.onlab.onos.store.service.DatabaseAdminService; |
| 45 | import org.onlab.onos.store.service.DatabaseService; | 47 | import org.onlab.onos.store.service.DatabaseService; |
| 48 | +import org.onlab.onos.store.service.Lock; | ||
| 49 | +import org.onlab.onos.store.service.LockService; | ||
| 46 | import org.onlab.onos.store.service.VersionedValue; | 50 | import org.onlab.onos.store.service.VersionedValue; |
| 47 | import org.slf4j.Logger; | 51 | import org.slf4j.Logger; |
| 48 | 52 | ||
| ... | @@ -72,6 +76,9 @@ public class FooComponent { | ... | @@ -72,6 +76,9 @@ public class FooComponent { |
| 72 | @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY) | 76 | @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY) |
| 73 | protected DatabaseService dbService; | 77 | protected DatabaseService dbService; |
| 74 | 78 | ||
| 79 | + @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY) | ||
| 80 | + protected LockService lockService; | ||
| 81 | + | ||
| 75 | private final ClusterEventListener clusterListener = new InnerClusterListener(); | 82 | private final ClusterEventListener clusterListener = new InnerClusterListener(); |
| 76 | private final DeviceListener deviceListener = new InnerDeviceListener(); | 83 | private final DeviceListener deviceListener = new InnerDeviceListener(); |
| 77 | private final IntentListener intentListener = new InnerIntentListener(); | 84 | private final IntentListener intentListener = new InnerIntentListener(); |
| ... | @@ -92,9 +99,10 @@ public class FooComponent { | ... | @@ -92,9 +99,10 @@ public class FooComponent { |
| 92 | log.info("Couldn't find DB service"); | 99 | log.info("Couldn't find DB service"); |
| 93 | } else { | 100 | } else { |
| 94 | log.info("Found DB service"); | 101 | log.info("Found DB service"); |
| 95 | - longIncrementor(); | 102 | + //longIncrementor(); |
| 96 | - executor.scheduleAtFixedRate(new LongIncrementor(), 1, 10, TimeUnit.SECONDS); | 103 | + //lockUnlock(); |
| 97 | - executor.scheduleAtFixedRate(new LongIncrementor(), 1, 10, TimeUnit.SECONDS); | 104 | + //executor.scheduleAtFixedRate(new LongIncrementor(), 1, 10, TimeUnit.SECONDS); |
| 105 | + //executor.scheduleAtFixedRate(new LongIncrementor(), 1, 10, TimeUnit.SECONDS); | ||
| 98 | } | 106 | } |
| 99 | log.info("Started"); | 107 | log.info("Started"); |
| 100 | } | 108 | } |
| ... | @@ -152,6 +160,31 @@ public class FooComponent { | ... | @@ -152,6 +160,31 @@ public class FooComponent { |
| 152 | } | 160 | } |
| 153 | } | 161 | } |
| 154 | 162 | ||
| 163 | + private void lockUnlock() { | ||
| 164 | + try { | ||
| 165 | + final String locksTable = "onos-locks"; | ||
| 166 | + | ||
| 167 | + if (!dbAdminService.listTables().contains(locksTable)) { | ||
| 168 | + dbAdminService.createTable(locksTable, 10000); | ||
| 169 | + } | ||
| 170 | + Lock lock = lockService.create("foo-bar"); | ||
| 171 | + log.info("Requesting lock"); | ||
| 172 | + lock.lock(10000); | ||
| 173 | + //try { | ||
| 174 | + //Thread.sleep(5000); | ||
| 175 | + //} catch (InterruptedException e) { | ||
| 176 | + // TODO Auto-generated catch block | ||
| 177 | + //e.printStackTrace(); | ||
| 178 | + //} | ||
| 179 | + log.info("Acquired Lock"); | ||
| 180 | + log.info("Do I have the lock: {} ", lock.isLocked()); | ||
| 181 | + //lock.unlock(); | ||
| 182 | + log.info("Do I have the lock: {} ", lock.isLocked()); | ||
| 183 | + } finally { | ||
| 184 | + log.info("Done"); | ||
| 185 | + } | ||
| 186 | + } | ||
| 187 | + | ||
| 155 | private void longIncrementor() { | 188 | private void longIncrementor() { |
| 156 | try { | 189 | try { |
| 157 | final String someTable = "admin"; | 190 | final String someTable = "admin"; | ... | ... |
| ... | @@ -38,6 +38,15 @@ public interface ClusterCommunicationService { | ... | @@ -38,6 +38,15 @@ public interface ClusterCommunicationService { |
| 38 | boolean broadcast(ClusterMessage message) throws IOException; | 38 | boolean broadcast(ClusterMessage message) throws IOException; |
| 39 | 39 | ||
| 40 | /** | 40 | /** |
| 41 | + * Broadcast a message to all controller nodes including self. | ||
| 42 | + * | ||
| 43 | + * @param message message to send | ||
| 44 | + * @return true if the message was sent successfully to all nodes; false otherwise. | ||
| 45 | + * @throws IOException when I/O exception of some sort has occurred | ||
| 46 | + */ | ||
| 47 | + boolean broadcastIncludeSelf(ClusterMessage message) throws IOException; | ||
| 48 | + | ||
| 49 | + /** | ||
| 41 | * Sends a message to the specified controller node. | 50 | * Sends a message to the specified controller node. |
| 42 | * | 51 | * |
| 43 | * @param message message to send | 52 | * @param message message to send | ... | ... |
| ... | @@ -2,6 +2,7 @@ package org.onlab.onos.store.service; | ... | @@ -2,6 +2,7 @@ package org.onlab.onos.store.service; |
| 2 | 2 | ||
| 3 | import java.util.List; | 3 | import java.util.List; |
| 4 | 4 | ||
| 5 | +import com.google.common.base.MoreObjects; | ||
| 5 | import com.google.common.collect.ImmutableList; | 6 | import com.google.common.collect.ImmutableList; |
| 6 | import com.google.common.collect.Lists; | 7 | import com.google.common.collect.Lists; |
| 7 | 8 | ||
| ... | @@ -41,6 +42,13 @@ public final class BatchReadRequest { | ... | @@ -41,6 +42,13 @@ public final class BatchReadRequest { |
| 41 | return readRequests; | 42 | return readRequests; |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 45 | + @Override | ||
| 46 | + public String toString() { | ||
| 47 | + return MoreObjects.toStringHelper(getClass()) | ||
| 48 | + .add("readRequests", readRequests) | ||
| 49 | + .toString(); | ||
| 50 | + } | ||
| 51 | + | ||
| 44 | /** | 52 | /** |
| 45 | * Builder for BatchReadRequest. | 53 | * Builder for BatchReadRequest. |
| 46 | */ | 54 | */ | ... | ... |
| ... | @@ -2,6 +2,7 @@ package org.onlab.onos.store.service; | ... | @@ -2,6 +2,7 @@ package org.onlab.onos.store.service; |
| 2 | 2 | ||
| 3 | import java.util.List; | 3 | import java.util.List; |
| 4 | 4 | ||
| 5 | +import com.google.common.base.MoreObjects; | ||
| 5 | import com.google.common.collect.ImmutableList; | 6 | import com.google.common.collect.ImmutableList; |
| 6 | 7 | ||
| 7 | /** | 8 | /** |
| ... | @@ -30,4 +31,11 @@ public class BatchReadResult { | ... | @@ -30,4 +31,11 @@ public class BatchReadResult { |
| 30 | public int batchSize() { | 31 | public int batchSize() { |
| 31 | return readResults.size(); | 32 | return readResults.size(); |
| 32 | } | 33 | } |
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + public String toString() { | ||
| 37 | + return MoreObjects.toStringHelper(getClass()) | ||
| 38 | + .add("readResults", readResults) | ||
| 39 | + .toString(); | ||
| 40 | + } | ||
| 33 | } | 41 | } | ... | ... |
| ... | @@ -2,6 +2,7 @@ package org.onlab.onos.store.service; | ... | @@ -2,6 +2,7 @@ package org.onlab.onos.store.service; |
| 2 | 2 | ||
| 3 | import java.util.List; | 3 | import java.util.List; |
| 4 | 4 | ||
| 5 | +import com.google.common.base.MoreObjects; | ||
| 5 | import com.google.common.collect.ImmutableList; | 6 | import com.google.common.collect.ImmutableList; |
| 6 | import com.google.common.collect.Lists; | 7 | import com.google.common.collect.Lists; |
| 7 | 8 | ||
| ... | @@ -41,6 +42,13 @@ public final class BatchWriteRequest { | ... | @@ -41,6 +42,13 @@ public final class BatchWriteRequest { |
| 41 | return writeRequests.size(); | 42 | return writeRequests.size(); |
| 42 | } | 43 | } |
| 43 | 44 | ||
| 45 | + @Override | ||
| 46 | + public String toString() { | ||
| 47 | + return MoreObjects.toStringHelper(getClass()) | ||
| 48 | + .add("writeRequests", writeRequests) | ||
| 49 | + .toString(); | ||
| 50 | + } | ||
| 51 | + | ||
| 44 | /** | 52 | /** |
| 45 | * Builder for BatchWriteRequest. | 53 | * Builder for BatchWriteRequest. |
| 46 | */ | 54 | */ | ... | ... |
| ... | @@ -2,6 +2,7 @@ package org.onlab.onos.store.service; | ... | @@ -2,6 +2,7 @@ package org.onlab.onos.store.service; |
| 2 | 2 | ||
| 3 | import java.util.List; | 3 | import java.util.List; |
| 4 | 4 | ||
| 5 | +import com.google.common.base.MoreObjects; | ||
| 5 | import com.google.common.collect.ImmutableList; | 6 | import com.google.common.collect.ImmutableList; |
| 6 | 7 | ||
| 7 | /** | 8 | /** |
| ... | @@ -43,4 +44,11 @@ public class BatchWriteResult { | ... | @@ -43,4 +44,11 @@ public class BatchWriteResult { |
| 43 | public int batchSize() { | 44 | public int batchSize() { |
| 44 | return writeResults.size(); | 45 | return writeResults.size(); |
| 45 | } | 46 | } |
| 47 | + | ||
| 48 | + @Override | ||
| 49 | + public String toString() { | ||
| 50 | + return MoreObjects.toStringHelper(getClass()) | ||
| 51 | + .add("writeResults", writeResults) | ||
| 52 | + .toString(); | ||
| 53 | + } | ||
| 46 | } | 54 | } | ... | ... |
| 1 | package org.onlab.onos.store.service; | 1 | package org.onlab.onos.store.service; |
| 2 | 2 | ||
| 3 | import java.util.Collection; | 3 | import java.util.Collection; |
| 4 | -import java.util.List; | 4 | +import java.util.Set; |
| 5 | 5 | ||
| 6 | import org.onlab.onos.cluster.ControllerNode; | 6 | import org.onlab.onos.cluster.ControllerNode; |
| 7 | 7 | ||
| ... | @@ -31,9 +31,9 @@ public interface DatabaseAdminService { | ... | @@ -31,9 +31,9 @@ public interface DatabaseAdminService { |
| 31 | 31 | ||
| 32 | /** | 32 | /** |
| 33 | * Lists all the tables in the database. | 33 | * Lists all the tables in the database. |
| 34 | - * @return list of table names. | 34 | + * @return set of table names. |
| 35 | */ | 35 | */ |
| 36 | - public List<String> listTables(); | 36 | + public Set<String> listTables(); |
| 37 | 37 | ||
| 38 | /** | 38 | /** |
| 39 | * Deletes a table from the database. | 39 | * Deletes a table from the database. | ... | ... |
| ... | @@ -64,6 +64,7 @@ public class ReadResult { | ... | @@ -64,6 +64,7 @@ public class ReadResult { |
| 64 | @Override | 64 | @Override |
| 65 | public String toString() { | 65 | public String toString() { |
| 66 | return MoreObjects.toStringHelper(getClass()) | 66 | return MoreObjects.toStringHelper(getClass()) |
| 67 | + .add("status", status) | ||
| 67 | .add("tableName", tableName) | 68 | .add("tableName", tableName) |
| 68 | .add("key", key) | 69 | .add("key", key) |
| 69 | .add("value", value) | 70 | .add("value", value) | ... | ... |
| ... | @@ -114,7 +114,7 @@ public class WriteRequest { | ... | @@ -114,7 +114,7 @@ public class WriteRequest { |
| 114 | */ | 114 | */ |
| 115 | public static WriteRequest removeIfVersionMatches(String tableName, String key, | 115 | public static WriteRequest removeIfVersionMatches(String tableName, String key, |
| 116 | long previousVersion) { | 116 | long previousVersion) { |
| 117 | - return new WriteRequest(REMOVE_IF_VALUE, tableName, key, | 117 | + return new WriteRequest(REMOVE_IF_VERSION, tableName, key, |
| 118 | null, previousVersion, null); | 118 | null, previousVersion, null); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| ... | @@ -129,7 +129,7 @@ public class WriteRequest { | ... | @@ -129,7 +129,7 @@ public class WriteRequest { |
| 129 | */ | 129 | */ |
| 130 | public static WriteRequest removeIfValueMatches(String tableName, String key, | 130 | public static WriteRequest removeIfValueMatches(String tableName, String key, |
| 131 | byte[] oldValue) { | 131 | byte[] oldValue) { |
| 132 | - return new WriteRequest(Type.REMOVE_IF_VALUE, tableName, key, | 132 | + return new WriteRequest(REMOVE_IF_VALUE, tableName, key, |
| 133 | null, ANY_VERSION, checkNotNull(oldValue)); | 133 | null, ANY_VERSION, checkNotNull(oldValue)); |
| 134 | } | 134 | } |
| 135 | 135 | ... | ... |
| ... | @@ -116,6 +116,15 @@ public class ClusterCommunicationManager | ... | @@ -116,6 +116,15 @@ public class ClusterCommunicationManager |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | @Override | 118 | @Override |
| 119 | + public boolean broadcastIncludeSelf(ClusterMessage message) throws IOException { | ||
| 120 | + boolean ok = true; | ||
| 121 | + for (ControllerNode node : clusterService.getNodes()) { | ||
| 122 | + ok = unicast(message, node.id()) && ok; | ||
| 123 | + } | ||
| 124 | + return ok; | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + @Override | ||
| 119 | public boolean multicast(ClusterMessage message, Set<NodeId> nodes) throws IOException { | 128 | public boolean multicast(ClusterMessage message, Set<NodeId> nodes) throws IOException { |
| 120 | boolean ok = true; | 129 | boolean ok = true; |
| 121 | final ControllerNode localNode = clusterService.getLocalNode(); | 130 | final ControllerNode localNode = clusterService.getLocalNode(); | ... | ... |
| ... | @@ -3,6 +3,7 @@ package org.onlab.onos.store.service.impl; | ... | @@ -3,6 +3,7 @@ package org.onlab.onos.store.service.impl; |
| 3 | import static com.google.common.base.Preconditions.checkNotNull; | 3 | import static com.google.common.base.Preconditions.checkNotNull; |
| 4 | 4 | ||
| 5 | import java.util.List; | 5 | import java.util.List; |
| 6 | +import java.util.Set; | ||
| 6 | import java.util.concurrent.CompletableFuture; | 7 | import java.util.concurrent.CompletableFuture; |
| 7 | import java.util.concurrent.ExecutionException; | 8 | import java.util.concurrent.ExecutionException; |
| 8 | 9 | ||
| ... | @@ -37,7 +38,7 @@ public class DatabaseClient { | ... | @@ -37,7 +38,7 @@ public class DatabaseClient { |
| 37 | 38 | ||
| 38 | public boolean createTable(String tableName, int ttlMillis) { | 39 | public boolean createTable(String tableName, int ttlMillis) { |
| 39 | 40 | ||
| 40 | - CompletableFuture<Boolean> future = copycat.submit("createTable", tableName, ttlMillis); | 41 | + CompletableFuture<Boolean> future = copycat.submit("createTableWithExpiration", tableName); |
| 41 | try { | 42 | try { |
| 42 | return future.get(); | 43 | return future.get(); |
| 43 | } catch (InterruptedException | ExecutionException e) { | 44 | } catch (InterruptedException | ExecutionException e) { |
| ... | @@ -65,9 +66,9 @@ public class DatabaseClient { | ... | @@ -65,9 +66,9 @@ public class DatabaseClient { |
| 65 | } | 66 | } |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | - public List<String> listTables() { | 69 | + public Set<String> listTables() { |
| 69 | 70 | ||
| 70 | - CompletableFuture<List<String>> future = copycat.submit("listTables"); | 71 | + CompletableFuture<Set<String>> future = copycat.submit("listTables"); |
| 71 | try { | 72 | try { |
| 72 | return future.get(); | 73 | return future.get(); |
| 73 | } catch (InterruptedException | ExecutionException e) { | 74 | } catch (InterruptedException | ExecutionException e) { | ... | ... |
| ... | @@ -33,7 +33,6 @@ import net.kuujo.copycat.event.LeaderElectEvent; | ... | @@ -33,7 +33,6 @@ import net.kuujo.copycat.event.LeaderElectEvent; |
| 33 | import org.onlab.onos.cluster.ControllerNode; | 33 | import org.onlab.onos.cluster.ControllerNode; |
| 34 | import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; | 34 | import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; |
| 35 | import org.onlab.onos.store.cluster.messaging.ClusterMessage; | 35 | import org.onlab.onos.store.cluster.messaging.ClusterMessage; |
| 36 | -import org.onlab.onos.store.cluster.messaging.MessageSubject; | ||
| 37 | import org.onlab.onos.store.service.DatabaseService; | 36 | import org.onlab.onos.store.service.DatabaseService; |
| 38 | import org.onlab.onos.store.service.VersionedValue; | 37 | import org.onlab.onos.store.service.VersionedValue; |
| 39 | import org.onlab.onos.store.service.impl.DatabaseStateMachine.State; | 38 | import org.onlab.onos.store.service.impl.DatabaseStateMachine.State; |
| ... | @@ -51,9 +50,6 @@ public class DatabaseEntryExpirationTracker implements | ... | @@ -51,9 +50,6 @@ public class DatabaseEntryExpirationTracker implements |
| 51 | 50 | ||
| 52 | private final Logger log = LoggerFactory.getLogger(getClass()); | 51 | private final Logger log = LoggerFactory.getLogger(getClass()); |
| 53 | 52 | ||
| 54 | - public static final MessageSubject DATABASE_UPDATES = new MessageSubject( | ||
| 55 | - "database-update-event"); | ||
| 56 | - | ||
| 57 | private final DatabaseService databaseService; | 53 | private final DatabaseService databaseService; |
| 58 | private final ClusterCommunicationService clusterCommunicator; | 54 | private final ClusterCommunicationService clusterCommunicator; |
| 59 | 55 | ||
| ... | @@ -61,9 +57,9 @@ public class DatabaseEntryExpirationTracker implements | ... | @@ -61,9 +57,9 @@ public class DatabaseEntryExpirationTracker implements |
| 61 | private final ControllerNode localNode; | 57 | private final ControllerNode localNode; |
| 62 | private final AtomicBoolean isLocalMemberLeader = new AtomicBoolean(false); | 58 | private final AtomicBoolean isLocalMemberLeader = new AtomicBoolean(false); |
| 63 | 59 | ||
| 64 | - private final Map<String, Map<DatabaseRow, VersionedValue>> tableEntryExpirationMap = new HashMap<>(); | 60 | + private final Map<String, Map<DatabaseRow, Long>> tableEntryExpirationMap = new HashMap<>(); |
| 65 | 61 | ||
| 66 | - private final ExpirationListener<DatabaseRow, VersionedValue> expirationObserver = new ExpirationObserver(); | 62 | + private final ExpirationListener<DatabaseRow, Long> expirationObserver = new ExpirationObserver(); |
| 67 | 63 | ||
| 68 | DatabaseEntryExpirationTracker( | 64 | DatabaseEntryExpirationTracker( |
| 69 | Member localMember, | 65 | Member localMember, |
| ... | @@ -78,31 +74,38 @@ public class DatabaseEntryExpirationTracker implements | ... | @@ -78,31 +74,38 @@ public class DatabaseEntryExpirationTracker implements |
| 78 | 74 | ||
| 79 | @Override | 75 | @Override |
| 80 | public void tableModified(TableModificationEvent event) { | 76 | public void tableModified(TableModificationEvent event) { |
| 77 | + log.debug("Received a table modification event {}", event); | ||
| 78 | + | ||
| 81 | if (!tableEntryExpirationMap.containsKey(event.tableName())) { | 79 | if (!tableEntryExpirationMap.containsKey(event.tableName())) { |
| 82 | return; | 80 | return; |
| 83 | } | 81 | } |
| 84 | 82 | ||
| 83 | + Map<DatabaseRow, Long> map = tableEntryExpirationMap.get(event.tableName()); | ||
| 85 | DatabaseRow row = new DatabaseRow(event.tableName(), event.key()); | 84 | DatabaseRow row = new DatabaseRow(event.tableName(), event.key()); |
| 86 | - Map<DatabaseRow, VersionedValue> map = tableEntryExpirationMap | 85 | + Long eventVersion = event.value().version(); |
| 87 | - .get(event.tableName()); | ||
| 88 | 86 | ||
| 89 | switch (event.type()) { | 87 | switch (event.type()) { |
| 90 | case ROW_DELETED: | 88 | case ROW_DELETED: |
| 89 | + map.remove(row, eventVersion); | ||
| 91 | if (isLocalMemberLeader.get()) { | 90 | if (isLocalMemberLeader.get()) { |
| 92 | try { | 91 | try { |
| 92 | + // FIXME: The broadcast message should be sent to self. | ||
| 93 | clusterCommunicator.broadcast(new ClusterMessage( | 93 | clusterCommunicator.broadcast(new ClusterMessage( |
| 94 | - localNode.id(), DATABASE_UPDATES, | 94 | + localNode.id(), DatabaseStateMachine.DATABASE_UPDATE_EVENTS, |
| 95 | DatabaseStateMachine.SERIALIZER.encode(event))); | 95 | DatabaseStateMachine.SERIALIZER.encode(event))); |
| 96 | } catch (IOException e) { | 96 | } catch (IOException e) { |
| 97 | - log.error( | 97 | + log.error("Failed to broadcast a database row deleted event.", e); |
| 98 | - "Failed to broadcast a database table modification event.", | ||
| 99 | - e); | ||
| 100 | } | 98 | } |
| 101 | } | 99 | } |
| 102 | break; | 100 | break; |
| 103 | case ROW_ADDED: | 101 | case ROW_ADDED: |
| 104 | case ROW_UPDATED: | 102 | case ROW_UPDATED: |
| 105 | - map.put(row, null); | 103 | + // To account for potential reordering of notifications, |
| 104 | + // check to make sure we are replacing an old version with a new version | ||
| 105 | + Long currentVersion = map.get(row); | ||
| 106 | + if (currentVersion == null || currentVersion < eventVersion) { | ||
| 107 | + map.put(row, eventVersion); | ||
| 108 | + } | ||
| 106 | break; | 109 | break; |
| 107 | default: | 110 | default: |
| 108 | break; | 111 | break; |
| ... | @@ -111,60 +114,56 @@ public class DatabaseEntryExpirationTracker implements | ... | @@ -111,60 +114,56 @@ public class DatabaseEntryExpirationTracker implements |
| 111 | 114 | ||
| 112 | @Override | 115 | @Override |
| 113 | public void tableCreated(TableMetadata metadata) { | 116 | public void tableCreated(TableMetadata metadata) { |
| 117 | + log.debug("Received a table created event {}", metadata); | ||
| 114 | if (metadata.expireOldEntries()) { | 118 | if (metadata.expireOldEntries()) { |
| 115 | tableEntryExpirationMap.put(metadata.tableName(), ExpiringMap.builder() | 119 | tableEntryExpirationMap.put(metadata.tableName(), ExpiringMap.builder() |
| 116 | - .expiration(metadata.ttlMillis(), TimeUnit.SECONDS) | 120 | + .expiration(metadata.ttlMillis(), TimeUnit.MILLISECONDS) |
| 117 | .expirationListener(expirationObserver) | 121 | .expirationListener(expirationObserver) |
| 118 | - // FIXME: make the expiration policy configurable. | 122 | + // TODO: make the expiration policy configurable. |
| 123 | + // Do we need to support expiration based on last access time? | ||
| 119 | .expirationPolicy(ExpirationPolicy.CREATED).build()); | 124 | .expirationPolicy(ExpirationPolicy.CREATED).build()); |
| 120 | } | 125 | } |
| 121 | } | 126 | } |
| 122 | 127 | ||
| 123 | @Override | 128 | @Override |
| 124 | public void tableDeleted(String tableName) { | 129 | public void tableDeleted(String tableName) { |
| 130 | + log.debug("Received a table deleted event for table ({})", tableName); | ||
| 125 | tableEntryExpirationMap.remove(tableName); | 131 | tableEntryExpirationMap.remove(tableName); |
| 126 | } | 132 | } |
| 127 | 133 | ||
| 128 | private class ExpirationObserver implements | 134 | private class ExpirationObserver implements |
| 129 | - ExpirationListener<DatabaseRow, VersionedValue> { | 135 | + ExpirationListener<DatabaseRow, Long> { |
| 130 | @Override | 136 | @Override |
| 131 | - public void expired(DatabaseRow key, VersionedValue value) { | 137 | + public void expired(DatabaseRow row, Long version) { |
| 138 | + Map<DatabaseRow, Long> map = tableEntryExpirationMap.get(row.tableName); | ||
| 132 | try { | 139 | try { |
| 133 | if (isLocalMemberLeader.get()) { | 140 | if (isLocalMemberLeader.get()) { |
| 134 | - if (!databaseService.removeIfVersionMatches(key.tableName, | 141 | + if (!databaseService.removeIfVersionMatches(row.tableName, |
| 135 | - key.key, value.version())) { | 142 | + row.key, version)) { |
| 136 | - log.info("Entry in the database changed before right its TTL expiration."); | 143 | + log.info("Entry in database was updated right before its expiration."); |
| 144 | + } else { | ||
| 145 | + log.info("Successfully expired old entry with key ({}) from table ({})", | ||
| 146 | + row.key, row.tableName); | ||
| 137 | } | 147 | } |
| 138 | } else { | 148 | } else { |
| 139 | - // If this node is not the current leader, we should never | 149 | + // Only the current leader will expire keys from database. |
| 140 | - // let the expiring entries drop off | 150 | + // Everyone else function as standby just in case they need to take over |
| 141 | - // Under stable conditions (i.e no leadership switch) the | ||
| 142 | - // current leader will initiate | ||
| 143 | - // a database remove and this instance will get notified | ||
| 144 | - // of a tableModification event causing it to remove from | ||
| 145 | - // the map. | ||
| 146 | - Map<DatabaseRow, VersionedValue> map = tableEntryExpirationMap | ||
| 147 | - .get(key.tableName); | ||
| 148 | if (map != null) { | 151 | if (map != null) { |
| 149 | - map.put(key, value); | 152 | + map.putIfAbsent(row, version); |
| 150 | } | 153 | } |
| 151 | } | 154 | } |
| 152 | 155 | ||
| 153 | } catch (Exception e) { | 156 | } catch (Exception e) { |
| 154 | - log.warn( | 157 | + log.warn("Failed to delete entry from the database after ttl " |
| 155 | - "Failed to delete entry from the database after ttl expiration. Will retry eviction", | 158 | + + "expiration. Operation will be retried.", e); |
| 156 | - e); | 159 | + map.putIfAbsent(row, version); |
| 157 | - tableEntryExpirationMap.get(key.tableName).put( | ||
| 158 | - new DatabaseRow(key.tableName, key.key), value); | ||
| 159 | } | 160 | } |
| 160 | } | 161 | } |
| 161 | } | 162 | } |
| 162 | 163 | ||
| 163 | @Override | 164 | @Override |
| 164 | public void handle(LeaderElectEvent event) { | 165 | public void handle(LeaderElectEvent event) { |
| 165 | - if (localMember.equals(event.leader())) { | 166 | + isLocalMemberLeader.set(localMember.equals(event.leader())); |
| 166 | - isLocalMemberLeader.set(true); | ||
| 167 | - } | ||
| 168 | } | 167 | } |
| 169 | 168 | ||
| 170 | /** | 169 | /** |
| ... | @@ -212,12 +211,12 @@ public class DatabaseEntryExpirationTracker implements | ... | @@ -212,12 +211,12 @@ public class DatabaseEntryExpirationTracker implements |
| 212 | continue; | 211 | continue; |
| 213 | } | 212 | } |
| 214 | 213 | ||
| 215 | - Map<DatabaseRow, VersionedValue> tableExpirationMap = ExpiringMap.builder() | 214 | + Map<DatabaseRow, Long> tableExpirationMap = ExpiringMap.builder() |
| 216 | .expiration(metadata.ttlMillis(), TimeUnit.MILLISECONDS) | 215 | .expiration(metadata.ttlMillis(), TimeUnit.MILLISECONDS) |
| 217 | .expirationListener(expirationObserver) | 216 | .expirationListener(expirationObserver) |
| 218 | .expirationPolicy(ExpirationPolicy.CREATED).build(); | 217 | .expirationPolicy(ExpirationPolicy.CREATED).build(); |
| 219 | for (Map.Entry<String, VersionedValue> entry : state.getTable(tableName).entrySet()) { | 218 | for (Map.Entry<String, VersionedValue> entry : state.getTable(tableName).entrySet()) { |
| 220 | - tableExpirationMap.put(new DatabaseRow(tableName, entry.getKey()), entry.getValue()); | 219 | + tableExpirationMap.put(new DatabaseRow(tableName, entry.getKey()), entry.getValue().version()); |
| 221 | } | 220 | } |
| 222 | 221 | ||
| 223 | tableEntryExpirationMap.put(tableName, tableExpirationMap); | 222 | tableEntryExpirationMap.put(tableName, tableExpirationMap); | ... | ... |
| ... | @@ -7,7 +7,6 @@ import java.io.IOException; | ... | @@ -7,7 +7,6 @@ import java.io.IOException; |
| 7 | import java.util.Collection; | 7 | import java.util.Collection; |
| 8 | import java.util.Collections; | 8 | import java.util.Collections; |
| 9 | import java.util.HashSet; | 9 | import java.util.HashSet; |
| 10 | -import java.util.List; | ||
| 11 | import java.util.Map; | 10 | import java.util.Map; |
| 12 | import java.util.Set; | 11 | import java.util.Set; |
| 13 | import java.util.concurrent.CountDownLatch; | 12 | import java.util.concurrent.CountDownLatch; |
| ... | @@ -19,6 +18,7 @@ import net.kuujo.copycat.cluster.Member; | ... | @@ -19,6 +18,7 @@ import net.kuujo.copycat.cluster.Member; |
| 19 | import net.kuujo.copycat.cluster.TcpCluster; | 18 | import net.kuujo.copycat.cluster.TcpCluster; |
| 20 | import net.kuujo.copycat.cluster.TcpClusterConfig; | 19 | import net.kuujo.copycat.cluster.TcpClusterConfig; |
| 21 | import net.kuujo.copycat.cluster.TcpMember; | 20 | import net.kuujo.copycat.cluster.TcpMember; |
| 21 | +import net.kuujo.copycat.event.LeaderElectEvent; | ||
| 22 | import net.kuujo.copycat.log.Log; | 22 | import net.kuujo.copycat.log.Log; |
| 23 | 23 | ||
| 24 | import org.apache.felix.scr.annotations.Activate; | 24 | import org.apache.felix.scr.annotations.Activate; |
| ... | @@ -160,18 +160,22 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { | ... | @@ -160,18 +160,22 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { |
| 160 | } | 160 | } |
| 161 | log.info("Starting cluster: {}", cluster); | 161 | log.info("Starting cluster: {}", cluster); |
| 162 | 162 | ||
| 163 | - | 163 | + DatabaseEntryExpirationTracker expirationTracker = |
| 164 | - DatabaseStateMachine stateMachine = new DatabaseStateMachine(); | ||
| 165 | - stateMachine.addEventListener( | ||
| 166 | new DatabaseEntryExpirationTracker( | 164 | new DatabaseEntryExpirationTracker( |
| 167 | clusterConfig.getLocalMember(), | 165 | clusterConfig.getLocalMember(), |
| 168 | clusterService.getLocalNode(), | 166 | clusterService.getLocalNode(), |
| 169 | clusterCommunicator, | 167 | clusterCommunicator, |
| 170 | - this)); | 168 | + this); |
| 169 | + | ||
| 170 | + DatabaseStateMachine stateMachine = new DatabaseStateMachine(); | ||
| 171 | + stateMachine.addEventListener(expirationTracker); | ||
| 171 | Log consensusLog = new MapDBLog(LOG_FILE_PREFIX + localNode.id(), | 172 | Log consensusLog = new MapDBLog(LOG_FILE_PREFIX + localNode.id(), |
| 172 | ClusterMessagingProtocol.SERIALIZER); | 173 | ClusterMessagingProtocol.SERIALIZER); |
| 173 | 174 | ||
| 174 | copycat = new Copycat(stateMachine, consensusLog, cluster, copycatMessagingProtocol); | 175 | copycat = new Copycat(stateMachine, consensusLog, cluster, copycatMessagingProtocol); |
| 176 | + | ||
| 177 | + copycat.event(LeaderElectEvent.class).registerHandler(expirationTracker); | ||
| 178 | + | ||
| 175 | copycat.start(); | 179 | copycat.start(); |
| 176 | 180 | ||
| 177 | client = new DatabaseClient(copycat); | 181 | client = new DatabaseClient(copycat); |
| ... | @@ -207,7 +211,7 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { | ... | @@ -207,7 +211,7 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { |
| 207 | } | 211 | } |
| 208 | 212 | ||
| 209 | @Override | 213 | @Override |
| 210 | - public List<String> listTables() { | 214 | + public Set<String> listTables() { |
| 211 | return client.listTables(); | 215 | return client.listTables(); |
| 212 | } | 216 | } |
| 213 | 217 | ... | ... |
| ... | @@ -30,6 +30,7 @@ import org.onlab.onos.store.service.WriteStatus; | ... | @@ -30,6 +30,7 @@ import org.onlab.onos.store.service.WriteStatus; |
| 30 | import org.onlab.util.KryoNamespace; | 30 | import org.onlab.util.KryoNamespace; |
| 31 | import org.slf4j.Logger; | 31 | import org.slf4j.Logger; |
| 32 | 32 | ||
| 33 | +import com.google.common.base.MoreObjects; | ||
| 33 | import com.google.common.collect.ImmutableSet; | 34 | import com.google.common.collect.ImmutableSet; |
| 34 | import com.google.common.collect.Lists; | 35 | import com.google.common.collect.Lists; |
| 35 | import com.google.common.collect.Maps; | 36 | import com.google.common.collect.Maps; |
| ... | @@ -65,6 +66,7 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -65,6 +66,7 @@ public class DatabaseStateMachine implements StateMachine { |
| 65 | .register(WriteStatus.class) | 66 | .register(WriteStatus.class) |
| 66 | // TODO: Move this out ? | 67 | // TODO: Move this out ? |
| 67 | .register(TableModificationEvent.class) | 68 | .register(TableModificationEvent.class) |
| 69 | + .register(TableModificationEvent.Type.class) | ||
| 68 | .register(ClusterMessagingProtocol.COMMON) | 70 | .register(ClusterMessagingProtocol.COMMON) |
| 69 | .build() | 71 | .build() |
| 70 | .populate(1); | 72 | .populate(1); |
| ... | @@ -85,7 +87,8 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -85,7 +87,8 @@ public class DatabaseStateMachine implements StateMachine { |
| 85 | } | 87 | } |
| 86 | 88 | ||
| 87 | @Command | 89 | @Command |
| 88 | - public boolean createTable(String tableName, int ttlMillis) { | 90 | + public boolean createTableWithExpiration(String tableName) { |
| 91 | + int ttlMillis = 10000; | ||
| 89 | TableMetadata metadata = new TableMetadata(tableName, ttlMillis); | 92 | TableMetadata metadata = new TableMetadata(tableName, ttlMillis); |
| 90 | return createTable(metadata); | 93 | return createTable(metadata); |
| 91 | } | 94 | } |
| ... | @@ -266,6 +269,7 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -266,6 +269,7 @@ public class DatabaseStateMachine implements StateMachine { |
| 266 | // notify listeners of table mod events. | 269 | // notify listeners of table mod events. |
| 267 | for (DatabaseUpdateEventListener listener : listeners) { | 270 | for (DatabaseUpdateEventListener listener : listeners) { |
| 268 | for (TableModificationEvent tableModificationEvent : tableModificationEvents) { | 271 | for (TableModificationEvent tableModificationEvent : tableModificationEvents) { |
| 272 | + log.info("Publishing table modification event: {}", tableModificationEvent); | ||
| 269 | listener.tableModified(tableModificationEvent); | 273 | listener.tableModified(tableModificationEvent); |
| 270 | } | 274 | } |
| 271 | } | 275 | } |
| ... | @@ -345,6 +349,15 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -345,6 +349,15 @@ public class DatabaseStateMachine implements StateMachine { |
| 345 | public int ttlMillis() { | 349 | public int ttlMillis() { |
| 346 | return ttlMillis; | 350 | return ttlMillis; |
| 347 | } | 351 | } |
| 352 | + | ||
| 353 | + @Override | ||
| 354 | + public String toString() { | ||
| 355 | + return MoreObjects.toStringHelper(getClass()) | ||
| 356 | + .add("tableName", tableName) | ||
| 357 | + .add("expireOldEntries", expireOldEntries) | ||
| 358 | + .add("ttlMillis", ttlMillis) | ||
| 359 | + .toString(); | ||
| 360 | + } | ||
| 348 | } | 361 | } |
| 349 | 362 | ||
| 350 | @Override | 363 | @Override | ... | ... |
| ... | @@ -80,6 +80,7 @@ public class DistributedLock implements Lock { | ... | @@ -80,6 +80,7 @@ public class DistributedLock implements Lock { |
| 80 | return false; | 80 | return false; |
| 81 | } | 81 | } |
| 82 | } | 82 | } |
| 83 | + isLocked.set(true); | ||
| 83 | lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis); | 84 | lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis); |
| 84 | return true; | 85 | return true; |
| 85 | } | 86 | } |
| ... | @@ -95,9 +96,11 @@ public class DistributedLock implements Lock { | ... | @@ -95,9 +96,11 @@ public class DistributedLock implements Lock { |
| 95 | if (DateTime.now().isAfter(lockExpirationTime)) { | 96 | if (DateTime.now().isAfter(lockExpirationTime)) { |
| 96 | isLocked.set(false); | 97 | isLocked.set(false); |
| 97 | return false; | 98 | return false; |
| 99 | + } else { | ||
| 100 | + return true; | ||
| 98 | } | 101 | } |
| 99 | } | 102 | } |
| 100 | - return true; | 103 | + return false; |
| 101 | } | 104 | } |
| 102 | 105 | ||
| 103 | @Override | 106 | @Override |
| ... | @@ -105,6 +108,7 @@ public class DistributedLock implements Lock { | ... | @@ -105,6 +108,7 @@ public class DistributedLock implements Lock { |
| 105 | if (!isLocked()) { | 108 | if (!isLocked()) { |
| 106 | return; | 109 | return; |
| 107 | } else { | 110 | } else { |
| 111 | + isLocked.set(false); | ||
| 108 | databaseService.removeIfValueMatches(DistributedLockManager.ONOS_LOCK_TABLE_NAME, path, lockId); | 112 | databaseService.removeIfValueMatches(DistributedLockManager.ONOS_LOCK_TABLE_NAME, path, lockId); |
| 109 | } | 113 | } |
| 110 | } | 114 | } | ... | ... |
| ... | @@ -94,6 +94,8 @@ public class DistributedLockManager implements LockService { | ... | @@ -94,6 +94,8 @@ public class DistributedLockManager implements LockService { |
| 94 | return; | 94 | return; |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | + log.info("Received a lock available event for path: {}", event.key()); | ||
| 98 | + | ||
| 97 | String path = event.key(); | 99 | String path = event.key(); |
| 98 | if (!locksToAcquire.containsKey(path)) { | 100 | if (!locksToAcquire.containsKey(path)) { |
| 99 | return; | 101 | return; | ... | ... |
| ... | @@ -2,6 +2,8 @@ package org.onlab.onos.store.service.impl; | ... | @@ -2,6 +2,8 @@ package org.onlab.onos.store.service.impl; |
| 2 | 2 | ||
| 3 | import org.onlab.onos.store.service.VersionedValue; | 3 | import org.onlab.onos.store.service.VersionedValue; |
| 4 | 4 | ||
| 5 | +import com.google.common.base.MoreObjects; | ||
| 6 | + | ||
| 5 | /** | 7 | /** |
| 6 | * A table modification event. | 8 | * A table modification event. |
| 7 | */ | 9 | */ |
| ... | @@ -9,7 +11,6 @@ public final class TableModificationEvent { | ... | @@ -9,7 +11,6 @@ public final class TableModificationEvent { |
| 9 | 11 | ||
| 10 | /** | 12 | /** |
| 11 | * Type of table modification event. | 13 | * Type of table modification event. |
| 12 | - * | ||
| 13 | */ | 14 | */ |
| 14 | public enum Type { | 15 | public enum Type { |
| 15 | ROW_ADDED, | 16 | ROW_ADDED, |
| ... | @@ -94,4 +95,14 @@ public final class TableModificationEvent { | ... | @@ -94,4 +95,14 @@ public final class TableModificationEvent { |
| 94 | public Type type() { | 95 | public Type type() { |
| 95 | return type; | 96 | return type; |
| 96 | } | 97 | } |
| 98 | + | ||
| 99 | + @Override | ||
| 100 | + public String toString() { | ||
| 101 | + return MoreObjects.toStringHelper(getClass()) | ||
| 102 | + .add("type", type) | ||
| 103 | + .add("tableName", tableName) | ||
| 104 | + .add("key", key) | ||
| 105 | + .add("version", value.version()) | ||
| 106 | + .toString(); | ||
| 107 | + } | ||
| 97 | } | 108 | } | ... | ... |
-
Please register or login to post a comment