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