Committed by
Gerrit Code Review
Merge "Support for expiring Database entries"
Showing
6 changed files
with
200 additions
and
49 deletions
| ... | @@ -20,6 +20,16 @@ public interface DatabaseAdminService { | ... | @@ -20,6 +20,16 @@ public interface DatabaseAdminService { |
| 20 | public boolean createTable(String name); | 20 | public boolean createTable(String name); |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| 23 | + * Creates a new table where last update time will be used to track and expire old entries. | ||
| 24 | + * Table creation is idempotent. Attempting to create a table | ||
| 25 | + * that already exists will be a noop. | ||
| 26 | + * @param name table name. | ||
| 27 | + * @param ttlMillis total duration in millis since last update time when entries will be expired. | ||
| 28 | + * @return true if the table was created by this call, false otherwise. | ||
| 29 | + */ | ||
| 30 | + public boolean createTable(String name, int ttlMillis); | ||
| 31 | + | ||
| 32 | + /** | ||
| 23 | * Lists all the tables in the database. | 33 | * Lists all the tables in the database. |
| 24 | * @return list of table names. | 34 | * @return list of table names. |
| 25 | */ | 35 | */ | ... | ... |
| ... | @@ -35,6 +35,16 @@ public class DatabaseClient { | ... | @@ -35,6 +35,16 @@ public class DatabaseClient { |
| 35 | } | 35 | } |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | + public boolean createTable(String tableName, int ttlMillis) { | ||
| 39 | + | ||
| 40 | + CompletableFuture<Boolean> future = copycat.submit("createTable", tableName, ttlMillis); | ||
| 41 | + try { | ||
| 42 | + return future.get(); | ||
| 43 | + } catch (InterruptedException | ExecutionException e) { | ||
| 44 | + throw new DatabaseException(e); | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | + | ||
| 38 | public void dropTable(String tableName) { | 48 | public void dropTable(String tableName) { |
| 39 | 49 | ||
| 40 | CompletableFuture<Void> future = copycat.submit("dropTable", tableName); | 50 | CompletableFuture<Void> future = copycat.submit("dropTable", tableName); | ... | ... |
| ... | @@ -30,12 +30,14 @@ import net.kuujo.copycat.cluster.Member; | ... | @@ -30,12 +30,14 @@ import net.kuujo.copycat.cluster.Member; |
| 30 | import net.kuujo.copycat.event.EventHandler; | 30 | import net.kuujo.copycat.event.EventHandler; |
| 31 | import net.kuujo.copycat.event.LeaderElectEvent; | 31 | import net.kuujo.copycat.event.LeaderElectEvent; |
| 32 | 32 | ||
| 33 | -import org.onlab.onos.cluster.ClusterService; | 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; | 36 | import org.onlab.onos.store.cluster.messaging.MessageSubject; |
| 37 | import org.onlab.onos.store.service.DatabaseService; | 37 | import org.onlab.onos.store.service.DatabaseService; |
| 38 | import org.onlab.onos.store.service.VersionedValue; | 38 | import org.onlab.onos.store.service.VersionedValue; |
| 39 | +import org.onlab.onos.store.service.impl.DatabaseStateMachine.State; | ||
| 40 | +import org.onlab.onos.store.service.impl.DatabaseStateMachine.TableMetadata; | ||
| 39 | import org.slf4j.Logger; | 41 | import org.slf4j.Logger; |
| 40 | import org.slf4j.LoggerFactory; | 42 | import org.slf4j.LoggerFactory; |
| 41 | 43 | ||
| ... | @@ -52,23 +54,34 @@ public class DatabaseEntryExpirationTracker implements | ... | @@ -52,23 +54,34 @@ public class DatabaseEntryExpirationTracker implements |
| 52 | public static final MessageSubject DATABASE_UPDATES = new MessageSubject( | 54 | public static final MessageSubject DATABASE_UPDATES = new MessageSubject( |
| 53 | "database-update-event"); | 55 | "database-update-event"); |
| 54 | 56 | ||
| 55 | - private DatabaseService databaseService; | 57 | + private final DatabaseService databaseService; |
| 56 | - private ClusterService cluster; | 58 | + private final ClusterCommunicationService clusterCommunicator; |
| 57 | - private ClusterCommunicationService clusterCommunicator; | ||
| 58 | 59 | ||
| 59 | private final Member localMember; | 60 | private final Member localMember; |
| 61 | + private final ControllerNode localNode; | ||
| 60 | private final AtomicBoolean isLocalMemberLeader = new AtomicBoolean(false); | 62 | private final AtomicBoolean isLocalMemberLeader = new AtomicBoolean(false); |
| 61 | 63 | ||
| 62 | private final Map<String, Map<DatabaseRow, VersionedValue>> tableEntryExpirationMap = new HashMap<>(); | 64 | private final Map<String, Map<DatabaseRow, VersionedValue>> tableEntryExpirationMap = new HashMap<>(); |
| 63 | 65 | ||
| 64 | private final ExpirationListener<DatabaseRow, VersionedValue> expirationObserver = new ExpirationObserver(); | 66 | private final ExpirationListener<DatabaseRow, VersionedValue> expirationObserver = new ExpirationObserver(); |
| 65 | 67 | ||
| 66 | - DatabaseEntryExpirationTracker(Member localMember) { | 68 | + DatabaseEntryExpirationTracker( |
| 69 | + Member localMember, | ||
| 70 | + ControllerNode localNode, | ||
| 71 | + ClusterCommunicationService clusterCommunicator, | ||
| 72 | + DatabaseService databaseService) { | ||
| 67 | this.localMember = localMember; | 73 | this.localMember = localMember; |
| 74 | + this.localNode = localNode; | ||
| 75 | + this.clusterCommunicator = clusterCommunicator; | ||
| 76 | + this.databaseService = databaseService; | ||
| 68 | } | 77 | } |
| 69 | 78 | ||
| 70 | @Override | 79 | @Override |
| 71 | public void tableModified(TableModificationEvent event) { | 80 | public void tableModified(TableModificationEvent event) { |
| 81 | + if (!tableEntryExpirationMap.containsKey(event.tableName())) { | ||
| 82 | + return; | ||
| 83 | + } | ||
| 84 | + | ||
| 72 | DatabaseRow row = new DatabaseRow(event.tableName(), event.key()); | 85 | DatabaseRow row = new DatabaseRow(event.tableName(), event.key()); |
| 73 | Map<DatabaseRow, VersionedValue> map = tableEntryExpirationMap | 86 | Map<DatabaseRow, VersionedValue> map = tableEntryExpirationMap |
| 74 | .get(event.tableName()); | 87 | .get(event.tableName()); |
| ... | @@ -77,8 +90,8 @@ public class DatabaseEntryExpirationTracker implements | ... | @@ -77,8 +90,8 @@ public class DatabaseEntryExpirationTracker implements |
| 77 | case ROW_DELETED: | 90 | case ROW_DELETED: |
| 78 | if (isLocalMemberLeader.get()) { | 91 | if (isLocalMemberLeader.get()) { |
| 79 | try { | 92 | try { |
| 80 | - clusterCommunicator.broadcast(new ClusterMessage(cluster | 93 | + clusterCommunicator.broadcast(new ClusterMessage( |
| 81 | - .getLocalNode().id(), DATABASE_UPDATES, | 94 | + localNode.id(), DATABASE_UPDATES, |
| 82 | DatabaseStateMachine.SERIALIZER.encode(event))); | 95 | DatabaseStateMachine.SERIALIZER.encode(event))); |
| 83 | } catch (IOException e) { | 96 | } catch (IOException e) { |
| 84 | log.error( | 97 | log.error( |
| ... | @@ -97,12 +110,10 @@ public class DatabaseEntryExpirationTracker implements | ... | @@ -97,12 +110,10 @@ public class DatabaseEntryExpirationTracker implements |
| 97 | } | 110 | } |
| 98 | 111 | ||
| 99 | @Override | 112 | @Override |
| 100 | - public void tableCreated(String tableName, int expirationTimeMillis) { | 113 | + public void tableCreated(TableMetadata metadata) { |
| 101 | - // make this explicit instead of relying on a negative value | 114 | + if (metadata.expireOldEntries()) { |
| 102 | - // to indicate no expiration. | 115 | + tableEntryExpirationMap.put(metadata.tableName(), ExpiringMap.builder() |
| 103 | - if (expirationTimeMillis > 0) { | 116 | + .expiration(metadata.ttlMillis(), TimeUnit.SECONDS) |
| 104 | - tableEntryExpirationMap.put(tableName, ExpiringMap.builder() | ||
| 105 | - .expiration(expirationTimeMillis, TimeUnit.SECONDS) | ||
| 106 | .expirationListener(expirationObserver) | 117 | .expirationListener(expirationObserver) |
| 107 | // FIXME: make the expiration policy configurable. | 118 | // FIXME: make the expiration policy configurable. |
| 108 | .expirationPolicy(ExpirationPolicy.CREATED).build()); | 119 | .expirationPolicy(ExpirationPolicy.CREATED).build()); |
| ... | @@ -188,4 +199,28 @@ public class DatabaseEntryExpirationTracker implements | ... | @@ -188,4 +199,28 @@ public class DatabaseEntryExpirationTracker implements |
| 188 | return Objects.hash(tableName, key); | 199 | return Objects.hash(tableName, key); |
| 189 | } | 200 | } |
| 190 | } | 201 | } |
| 202 | + | ||
| 203 | + @Override | ||
| 204 | + public void snapshotInstalled(State state) { | ||
| 205 | + if (!tableEntryExpirationMap.isEmpty()) { | ||
| 206 | + return; | ||
| 207 | + } | ||
| 208 | + for (String tableName : state.getTableNames()) { | ||
| 209 | + | ||
| 210 | + TableMetadata metadata = state.getTableMetadata(tableName); | ||
| 211 | + if (!metadata.expireOldEntries()) { | ||
| 212 | + continue; | ||
| 213 | + } | ||
| 214 | + | ||
| 215 | + Map<DatabaseRow, VersionedValue> tableExpirationMap = ExpiringMap.builder() | ||
| 216 | + .expiration(metadata.ttlMillis(), TimeUnit.MILLISECONDS) | ||
| 217 | + .expirationListener(expirationObserver) | ||
| 218 | + .expirationPolicy(ExpirationPolicy.CREATED).build(); | ||
| 219 | + for (Map.Entry<String, VersionedValue> entry : state.getTable(tableName).entrySet()) { | ||
| 220 | + tableExpirationMap.put(new DatabaseRow(tableName, entry.getKey()), entry.getValue()); | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + tableEntryExpirationMap.put(tableName, tableExpirationMap); | ||
| 224 | + } | ||
| 225 | + } | ||
| 191 | } | 226 | } | ... | ... |
| ... | @@ -14,7 +14,6 @@ import java.util.concurrent.CountDownLatch; | ... | @@ -14,7 +14,6 @@ import java.util.concurrent.CountDownLatch; |
| 14 | import java.util.concurrent.TimeUnit; | 14 | import java.util.concurrent.TimeUnit; |
| 15 | 15 | ||
| 16 | import net.kuujo.copycat.Copycat; | 16 | import net.kuujo.copycat.Copycat; |
| 17 | -import net.kuujo.copycat.StateMachine; | ||
| 18 | import net.kuujo.copycat.cluster.ClusterConfig; | 17 | import net.kuujo.copycat.cluster.ClusterConfig; |
| 19 | import net.kuujo.copycat.cluster.Member; | 18 | import net.kuujo.copycat.cluster.Member; |
| 20 | import net.kuujo.copycat.cluster.TcpCluster; | 19 | import net.kuujo.copycat.cluster.TcpCluster; |
| ... | @@ -34,6 +33,7 @@ import org.onlab.onos.cluster.ClusterService; | ... | @@ -34,6 +33,7 @@ import org.onlab.onos.cluster.ClusterService; |
| 34 | import org.onlab.onos.cluster.ControllerNode; | 33 | import org.onlab.onos.cluster.ControllerNode; |
| 35 | import org.onlab.onos.cluster.DefaultControllerNode; | 34 | import org.onlab.onos.cluster.DefaultControllerNode; |
| 36 | import org.onlab.onos.cluster.NodeId; | 35 | import org.onlab.onos.cluster.NodeId; |
| 36 | +import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; | ||
| 37 | import org.onlab.onos.store.service.BatchReadRequest; | 37 | import org.onlab.onos.store.service.BatchReadRequest; |
| 38 | import org.onlab.onos.store.service.BatchReadResult; | 38 | import org.onlab.onos.store.service.BatchReadResult; |
| 39 | import org.onlab.onos.store.service.BatchWriteRequest; | 39 | import org.onlab.onos.store.service.BatchWriteRequest; |
| ... | @@ -65,6 +65,9 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { | ... | @@ -65,6 +65,9 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { |
| 65 | protected ClusterService clusterService; | 65 | protected ClusterService clusterService; |
| 66 | 66 | ||
| 67 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 67 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 68 | + protected ClusterCommunicationService clusterCommunicator; | ||
| 69 | + | ||
| 70 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 68 | protected DatabaseProtocolService copycatMessagingProtocol; | 71 | protected DatabaseProtocolService copycatMessagingProtocol; |
| 69 | 72 | ||
| 70 | // FIXME: point to appropriate path | 73 | // FIXME: point to appropriate path |
| ... | @@ -158,7 +161,13 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { | ... | @@ -158,7 +161,13 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { |
| 158 | log.info("Starting cluster: {}", cluster); | 161 | log.info("Starting cluster: {}", cluster); |
| 159 | 162 | ||
| 160 | 163 | ||
| 161 | - StateMachine stateMachine = new DatabaseStateMachine(); | 164 | + DatabaseStateMachine stateMachine = new DatabaseStateMachine(); |
| 165 | + stateMachine.addEventListener( | ||
| 166 | + new DatabaseEntryExpirationTracker( | ||
| 167 | + clusterConfig.getLocalMember(), | ||
| 168 | + clusterService.getLocalNode(), | ||
| 169 | + clusterCommunicator, | ||
| 170 | + this)); | ||
| 162 | Log consensusLog = new MapDBLog(LOG_FILE_PREFIX + localNode.id(), | 171 | Log consensusLog = new MapDBLog(LOG_FILE_PREFIX + localNode.id(), |
| 163 | ClusterMessagingProtocol.SERIALIZER); | 172 | ClusterMessagingProtocol.SERIALIZER); |
| 164 | 173 | ||
| ... | @@ -183,6 +192,11 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { | ... | @@ -183,6 +192,11 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { |
| 183 | } | 192 | } |
| 184 | 193 | ||
| 185 | @Override | 194 | @Override |
| 195 | + public boolean createTable(String name, int ttlMillis) { | ||
| 196 | + return client.createTable(name, ttlMillis); | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + @Override | ||
| 186 | public void dropTable(String name) { | 200 | public void dropTable(String name) { |
| 187 | client.dropTable(name); | 201 | client.dropTable(name); |
| 188 | } | 202 | } | ... | ... |
| ... | @@ -30,9 +30,10 @@ import org.onlab.onos.store.service.WriteStatus; | ... | @@ -30,9 +30,10 @@ 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.collect.ImmutableList; | 33 | +import com.google.common.collect.ImmutableSet; |
| 34 | import com.google.common.collect.Lists; | 34 | import com.google.common.collect.Lists; |
| 35 | import com.google.common.collect.Maps; | 35 | import com.google.common.collect.Maps; |
| 36 | +import com.google.common.collect.Sets; | ||
| 36 | import com.google.common.io.ByteStreams; | 37 | import com.google.common.io.ByteStreams; |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| ... | @@ -57,6 +58,7 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -57,6 +58,7 @@ public class DatabaseStateMachine implements StateMachine { |
| 57 | serializerPool = KryoNamespace.newBuilder() | 58 | serializerPool = KryoNamespace.newBuilder() |
| 58 | .register(VersionedValue.class) | 59 | .register(VersionedValue.class) |
| 59 | .register(State.class) | 60 | .register(State.class) |
| 61 | + .register(TableMetadata.class) | ||
| 60 | .register(BatchReadRequest.class) | 62 | .register(BatchReadRequest.class) |
| 61 | .register(BatchWriteRequest.class) | 63 | .register(BatchWriteRequest.class) |
| 62 | .register(ReadStatus.class) | 64 | .register(ReadStatus.class) |
| ... | @@ -69,7 +71,7 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -69,7 +71,7 @@ public class DatabaseStateMachine implements StateMachine { |
| 69 | } | 71 | } |
| 70 | }; | 72 | }; |
| 71 | 73 | ||
| 72 | - private final List<DatabaseUpdateEventListener> listeners = Lists.newLinkedList(); | 74 | + private final Set<DatabaseUpdateEventListener> listeners = Sets.newIdentityHashSet(); |
| 73 | 75 | ||
| 74 | // durable internal state of the database. | 76 | // durable internal state of the database. |
| 75 | private State state = new State(); | 77 | private State state = new State(); |
| ... | @@ -78,34 +80,31 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -78,34 +80,31 @@ public class DatabaseStateMachine implements StateMachine { |
| 78 | 80 | ||
| 79 | @Command | 81 | @Command |
| 80 | public boolean createTable(String tableName) { | 82 | public boolean createTable(String tableName) { |
| 81 | - Map<String, VersionedValue> existingTable = | 83 | + TableMetadata metadata = new TableMetadata(tableName); |
| 82 | - state.getTables().putIfAbsent(tableName, Maps.newHashMap()); | 84 | + return createTable(metadata); |
| 83 | - if (existingTable == null) { | ||
| 84 | - for (DatabaseUpdateEventListener listener : listeners) { | ||
| 85 | - listener.tableCreated(tableName, Integer.MAX_VALUE); | ||
| 86 | } | 85 | } |
| 87 | - return true; | 86 | + |
| 87 | + @Command | ||
| 88 | + public boolean createTable(String tableName, int ttlMillis) { | ||
| 89 | + TableMetadata metadata = new TableMetadata(tableName, ttlMillis); | ||
| 90 | + return createTable(metadata); | ||
| 88 | } | 91 | } |
| 92 | + | ||
| 93 | + private boolean createTable(TableMetadata metadata) { | ||
| 94 | + Map<String, VersionedValue> existingTable = state.getTable(metadata.tableName()); | ||
| 95 | + if (existingTable != null) { | ||
| 89 | return false; | 96 | return false; |
| 90 | } | 97 | } |
| 91 | - | 98 | + state.createTable(metadata); |
| 92 | - @Command | ||
| 93 | - public boolean createTable(String tableName, int expirationTimeMillis) { | ||
| 94 | - Map<String, VersionedValue> existingTable = | ||
| 95 | - state.getTables().putIfAbsent(tableName, Maps.newHashMap()); | ||
| 96 | - if (existingTable == null) { | ||
| 97 | for (DatabaseUpdateEventListener listener : listeners) { | 99 | for (DatabaseUpdateEventListener listener : listeners) { |
| 98 | - listener.tableCreated(tableName, expirationTimeMillis); | 100 | + listener.tableCreated(metadata); |
| 99 | } | 101 | } |
| 100 | return true; | 102 | return true; |
| 101 | } | 103 | } |
| 102 | - return false; | ||
| 103 | - } | ||
| 104 | 104 | ||
| 105 | @Command | 105 | @Command |
| 106 | public boolean dropTable(String tableName) { | 106 | public boolean dropTable(String tableName) { |
| 107 | - Map<String, VersionedValue> table = state.getTables().remove(tableName); | 107 | + if (state.removeTable(tableName)) { |
| 108 | - if (table != null) { | ||
| 109 | for (DatabaseUpdateEventListener listener : listeners) { | 108 | for (DatabaseUpdateEventListener listener : listeners) { |
| 110 | listener.tableDeleted(tableName); | 109 | listener.tableDeleted(tableName); |
| 111 | } | 110 | } |
| ... | @@ -116,8 +115,8 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -116,8 +115,8 @@ public class DatabaseStateMachine implements StateMachine { |
| 116 | 115 | ||
| 117 | @Command | 116 | @Command |
| 118 | public boolean dropAllTables() { | 117 | public boolean dropAllTables() { |
| 119 | - Set<String> tableNames = state.getTables().keySet(); | 118 | + Set<String> tableNames = state.getTableNames(); |
| 120 | - state.getTables().clear(); | 119 | + state.removeAllTables(); |
| 121 | for (DatabaseUpdateEventListener listener : listeners) { | 120 | for (DatabaseUpdateEventListener listener : listeners) { |
| 122 | for (String tableName : tableNames) { | 121 | for (String tableName : tableNames) { |
| 123 | listener.tableDeleted(tableName); | 122 | listener.tableDeleted(tableName); |
| ... | @@ -127,15 +126,15 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -127,15 +126,15 @@ public class DatabaseStateMachine implements StateMachine { |
| 127 | } | 126 | } |
| 128 | 127 | ||
| 129 | @Query | 128 | @Query |
| 130 | - public List<String> listTables() { | 129 | + public Set<String> listTables() { |
| 131 | - return ImmutableList.copyOf(state.getTables().keySet()); | 130 | + return ImmutableSet.copyOf(state.getTableNames()); |
| 132 | } | 131 | } |
| 133 | 132 | ||
| 134 | @Query | 133 | @Query |
| 135 | public List<ReadResult> read(BatchReadRequest batchRequest) { | 134 | public List<ReadResult> read(BatchReadRequest batchRequest) { |
| 136 | List<ReadResult> results = new ArrayList<>(batchRequest.batchSize()); | 135 | List<ReadResult> results = new ArrayList<>(batchRequest.batchSize()); |
| 137 | for (ReadRequest request : batchRequest.getAsList()) { | 136 | for (ReadRequest request : batchRequest.getAsList()) { |
| 138 | - Map<String, VersionedValue> table = state.getTables().get(request.tableName()); | 137 | + Map<String, VersionedValue> table = state.getTable(request.tableName()); |
| 139 | if (table == null) { | 138 | if (table == null) { |
| 140 | results.add(new ReadResult(ReadStatus.NO_SUCH_TABLE, request.tableName(), request.key(), null)); | 139 | results.add(new ReadResult(ReadStatus.NO_SUCH_TABLE, request.tableName(), request.key(), null)); |
| 141 | continue; | 140 | continue; |
| ... | @@ -186,7 +185,7 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -186,7 +185,7 @@ public class DatabaseStateMachine implements StateMachine { |
| 186 | boolean abort = false; | 185 | boolean abort = false; |
| 187 | List<WriteStatus> validationResults = new ArrayList<>(batchRequest.batchSize()); | 186 | List<WriteStatus> validationResults = new ArrayList<>(batchRequest.batchSize()); |
| 188 | for (WriteRequest request : batchRequest.getAsList()) { | 187 | for (WriteRequest request : batchRequest.getAsList()) { |
| 189 | - Map<String, VersionedValue> table = state.getTables().get(request.tableName()); | 188 | + Map<String, VersionedValue> table = state.getTable(request.tableName()); |
| 190 | if (table == null) { | 189 | if (table == null) { |
| 191 | validationResults.add(WriteStatus.NO_SUCH_TABLE); | 190 | validationResults.add(WriteStatus.NO_SUCH_TABLE); |
| 192 | abort = true; | 191 | abort = true; |
| ... | @@ -218,7 +217,7 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -218,7 +217,7 @@ public class DatabaseStateMachine implements StateMachine { |
| 218 | 217 | ||
| 219 | // apply changes | 218 | // apply changes |
| 220 | for (WriteRequest request : batchRequest.getAsList()) { | 219 | for (WriteRequest request : batchRequest.getAsList()) { |
| 221 | - Map<String, VersionedValue> table = state.getTables().get(request.tableName()); | 220 | + Map<String, VersionedValue> table = state.getTable(request.tableName()); |
| 222 | 221 | ||
| 223 | TableModificationEvent tableModificationEvent = null; | 222 | TableModificationEvent tableModificationEvent = null; |
| 224 | // FIXME: If this method could be called by multiple thread, | 223 | // FIXME: If this method could be called by multiple thread, |
| ... | @@ -274,19 +273,78 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -274,19 +273,78 @@ public class DatabaseStateMachine implements StateMachine { |
| 274 | return results; | 273 | return results; |
| 275 | } | 274 | } |
| 276 | 275 | ||
| 277 | - public class State { | 276 | + public static class State { |
| 278 | 277 | ||
| 279 | - private final Map<String, Map<String, VersionedValue>> tables = | 278 | + private final Map<String, TableMetadata> tableMetadata = Maps.newHashMap(); |
| 280 | - Maps.newHashMap(); | 279 | + private final Map<String, Map<String, VersionedValue>> tableData = Maps.newHashMap(); |
| 281 | private long versionCounter = 1; | 280 | private long versionCounter = 1; |
| 282 | 281 | ||
| 283 | - Map<String, Map<String, VersionedValue>> getTables() { | 282 | + public Map<String, VersionedValue> getTable(String tableName) { |
| 284 | - return tables; | 283 | + return tableData.get(tableName); |
| 284 | + } | ||
| 285 | + | ||
| 286 | + void createTable(TableMetadata metadata) { | ||
| 287 | + tableMetadata.put(metadata.tableName, metadata); | ||
| 288 | + tableData.put(metadata.tableName, Maps.newHashMap()); | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + TableMetadata getTableMetadata(String tableName) { | ||
| 292 | + return tableMetadata.get(tableName); | ||
| 285 | } | 293 | } |
| 286 | 294 | ||
| 287 | long nextVersion() { | 295 | long nextVersion() { |
| 288 | return versionCounter++; | 296 | return versionCounter++; |
| 289 | } | 297 | } |
| 298 | + | ||
| 299 | + Set<String> getTableNames() { | ||
| 300 | + return ImmutableSet.copyOf(tableMetadata.keySet()); | ||
| 301 | + } | ||
| 302 | + | ||
| 303 | + | ||
| 304 | + boolean removeTable(String tableName) { | ||
| 305 | + if (!tableMetadata.containsKey(tableName)) { | ||
| 306 | + return false; | ||
| 307 | + } | ||
| 308 | + tableMetadata.remove(tableName); | ||
| 309 | + tableData.remove(tableName); | ||
| 310 | + return true; | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + void removeAllTables() { | ||
| 314 | + tableMetadata.clear(); | ||
| 315 | + tableData.clear(); | ||
| 316 | + } | ||
| 317 | + } | ||
| 318 | + | ||
| 319 | + public static class TableMetadata { | ||
| 320 | + private final String tableName; | ||
| 321 | + private final boolean expireOldEntries; | ||
| 322 | + private final int ttlMillis; | ||
| 323 | + | ||
| 324 | + public TableMetadata(String tableName) { | ||
| 325 | + this.tableName = tableName; | ||
| 326 | + this.expireOldEntries = false; | ||
| 327 | + this.ttlMillis = Integer.MAX_VALUE; | ||
| 328 | + | ||
| 329 | + } | ||
| 330 | + | ||
| 331 | + public TableMetadata(String tableName, int ttlMillis) { | ||
| 332 | + this.tableName = tableName; | ||
| 333 | + this.expireOldEntries = true; | ||
| 334 | + this.ttlMillis = ttlMillis; | ||
| 335 | + } | ||
| 336 | + | ||
| 337 | + public String tableName() { | ||
| 338 | + return tableName; | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + public boolean expireOldEntries() { | ||
| 342 | + return expireOldEntries; | ||
| 343 | + } | ||
| 344 | + | ||
| 345 | + public int ttlMillis() { | ||
| 346 | + return ttlMillis; | ||
| 347 | + } | ||
| 290 | } | 348 | } |
| 291 | 349 | ||
| 292 | @Override | 350 | @Override |
| ... | @@ -319,13 +377,30 @@ public class DatabaseStateMachine implements StateMachine { | ... | @@ -319,13 +377,30 @@ public class DatabaseStateMachine implements StateMachine { |
| 319 | } else { | 377 | } else { |
| 320 | this.state = SERIALIZER.decode(data); | 378 | this.state = SERIALIZER.decode(data); |
| 321 | } | 379 | } |
| 380 | + | ||
| 381 | + // FIXME: synchronize. | ||
| 382 | + for (DatabaseUpdateEventListener listener : listeners) { | ||
| 383 | + listener.snapshotInstalled(state); | ||
| 384 | + } | ||
| 322 | } catch (Exception e) { | 385 | } catch (Exception e) { |
| 323 | log.error("Failed to install from snapshot", e); | 386 | log.error("Failed to install from snapshot", e); |
| 324 | throw new SnapshotException(e); | 387 | throw new SnapshotException(e); |
| 325 | } | 388 | } |
| 326 | } | 389 | } |
| 327 | 390 | ||
| 391 | + /** | ||
| 392 | + * Adds specified DatabaseUpdateEventListener. | ||
| 393 | + * @param listener listener to add | ||
| 394 | + */ | ||
| 328 | public void addEventListener(DatabaseUpdateEventListener listener) { | 395 | public void addEventListener(DatabaseUpdateEventListener listener) { |
| 329 | listeners.add(listener); | 396 | listeners.add(listener); |
| 330 | } | 397 | } |
| 398 | + | ||
| 399 | + /** | ||
| 400 | + * Removes specified DatabaseUpdateEventListener. | ||
| 401 | + * @param listener listener to remove | ||
| 402 | + */ | ||
| 403 | + public void removeEventListener(DatabaseUpdateEventListener listener) { | ||
| 404 | + listeners.remove(listener); | ||
| 405 | + } | ||
| 331 | } | 406 | } | ... | ... |
| ... | @@ -16,6 +16,8 @@ | ... | @@ -16,6 +16,8 @@ |
| 16 | 16 | ||
| 17 | package org.onlab.onos.store.service.impl; | 17 | package org.onlab.onos.store.service.impl; |
| 18 | 18 | ||
| 19 | +import org.onlab.onos.store.service.impl.DatabaseStateMachine.TableMetadata; | ||
| 20 | + | ||
| 19 | /** | 21 | /** |
| 20 | * Interface of database update event listeners. | 22 | * Interface of database update event listeners. |
| 21 | */ | 23 | */ |
| ... | @@ -29,14 +31,19 @@ public interface DatabaseUpdateEventListener { | ... | @@ -29,14 +31,19 @@ public interface DatabaseUpdateEventListener { |
| 29 | 31 | ||
| 30 | /** | 32 | /** |
| 31 | * Notifies listeners of a table created event. | 33 | * Notifies listeners of a table created event. |
| 32 | - * @param tableName name of the table created | 34 | + * @param metadata metadata for the created table. |
| 33 | - * @param expirationTimeMillis TTL for entries added to the table (measured since last update time) | ||
| 34 | */ | 35 | */ |
| 35 | - public void tableCreated(String tableName, int expirationTimeMillis); | 36 | + public void tableCreated(TableMetadata metadata); |
| 36 | 37 | ||
| 37 | /** | 38 | /** |
| 38 | * Notifies listeners of a table deleted event. | 39 | * Notifies listeners of a table deleted event. |
| 39 | * @param tableName name of the table deleted | 40 | * @param tableName name of the table deleted |
| 40 | */ | 41 | */ |
| 41 | public void tableDeleted(String tableName); | 42 | public void tableDeleted(String tableName); |
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * Notifies listeners of a snapshot installation event. | ||
| 46 | + * @param snapshotState installed snapshot state. | ||
| 47 | + */ | ||
| 48 | + public void snapshotInstalled(DatabaseStateMachine.State snapshotState); | ||
| 42 | } | 49 | } | ... | ... |
-
Please register or login to post a comment