Support for expiring Database entries
Registering database entry expiration tracker with DatabaseStateMachine Support for publishing database state machine snapshot installation events. Expiry tracker will listen to these events to bootstrap its local state. Change-Id: I8bf22c8d7bab38624341350ccc083c5ca2fcb117
Showing
6 changed files
with
205 additions
and
54 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 | } |
... | @@ -418,4 +432,4 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { | ... | @@ -418,4 +432,4 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { |
418 | } | 432 | } |
419 | return null; | 433 | return null; |
420 | } | 434 | } |
421 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
435 | +} | ... | ... |
... | @@ -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 | - } | ||
87 | - return true; | ||
88 | - } | ||
89 | - return false; | ||
90 | } | 85 | } |
91 | 86 | ||
92 | @Command | 87 | @Command |
93 | - public boolean createTable(String tableName, int expirationTimeMillis) { | 88 | + public boolean createTable(String tableName, int ttlMillis) { |
94 | - Map<String, VersionedValue> existingTable = | 89 | + TableMetadata metadata = new TableMetadata(tableName, ttlMillis); |
95 | - state.getTables().putIfAbsent(tableName, Maps.newHashMap()); | 90 | + return createTable(metadata); |
96 | - if (existingTable == null) { | 91 | + } |
97 | - for (DatabaseUpdateEventListener listener : listeners) { | 92 | + |
98 | - listener.tableCreated(tableName, expirationTimeMillis); | 93 | + private boolean createTable(TableMetadata metadata) { |
99 | - } | 94 | + Map<String, VersionedValue> existingTable = state.getTable(metadata.tableName()); |
100 | - return true; | 95 | + if (existingTable != null) { |
96 | + return false; | ||
101 | } | 97 | } |
102 | - return false; | 98 | + state.createTable(metadata); |
99 | + for (DatabaseUpdateEventListener listener : listeners) { | ||
100 | + listener.tableCreated(metadata); | ||
101 | + } | ||
102 | + return true; | ||
103 | } | 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); |
42 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
43 | + | ||
44 | + /** | ||
45 | + * Notifies listeners of a snapshot installation event. | ||
46 | + * @param snapshotState installed snapshot state. | ||
47 | + */ | ||
48 | + public void snapshotInstalled(DatabaseStateMachine.State snapshotState); | ||
49 | +} | ... | ... |
-
Please register or login to post a comment