Committed by
Gerrit Code Review
adding group garbage collection functionality
If a group has a reference count of zero for more than a configurable timeout, it is garbage collected. This feature can be deactivated by component config. Change-Id: I254d62a90ef7ac8d2ce2f406b67957455a5bf4d0
Showing
5 changed files
with
108 additions
and
12 deletions
... | @@ -35,6 +35,7 @@ public class DefaultGroup extends DefaultGroupDescription | ... | @@ -35,6 +35,7 @@ public class DefaultGroup extends DefaultGroupDescription |
35 | private long bytes; | 35 | private long bytes; |
36 | private long referenceCount; | 36 | private long referenceCount; |
37 | private GroupId id; | 37 | private GroupId id; |
38 | + private int age; | ||
38 | 39 | ||
39 | /** | 40 | /** |
40 | * Initializes default values. | 41 | * Initializes default values. |
... | @@ -48,6 +49,7 @@ public class DefaultGroup extends DefaultGroupDescription | ... | @@ -48,6 +49,7 @@ public class DefaultGroup extends DefaultGroupDescription |
48 | packets = 0; | 49 | packets = 0; |
49 | bytes = 0; | 50 | bytes = 0; |
50 | referenceCount = 0; | 51 | referenceCount = 0; |
52 | + age = 0; | ||
51 | } | 53 | } |
52 | 54 | ||
53 | /** | 55 | /** |
... | @@ -128,6 +130,11 @@ public class DefaultGroup extends DefaultGroupDescription | ... | @@ -128,6 +130,11 @@ public class DefaultGroup extends DefaultGroupDescription |
128 | return this.bytes; | 130 | return this.bytes; |
129 | } | 131 | } |
130 | 132 | ||
133 | + @Override | ||
134 | + public int age() { | ||
135 | + return age; | ||
136 | + } | ||
137 | + | ||
131 | /** | 138 | /** |
132 | * Sets the new state for this entry. | 139 | * Sets the new state for this entry. |
133 | * | 140 | * |
... | @@ -171,6 +178,11 @@ public class DefaultGroup extends DefaultGroupDescription | ... | @@ -171,6 +178,11 @@ public class DefaultGroup extends DefaultGroupDescription |
171 | @Override | 178 | @Override |
172 | public void setReferenceCount(long referenceCount) { | 179 | public void setReferenceCount(long referenceCount) { |
173 | this.referenceCount = referenceCount; | 180 | this.referenceCount = referenceCount; |
181 | + if (referenceCount == 0) { | ||
182 | + age++; | ||
183 | + } else { | ||
184 | + age = 0; | ||
185 | + } | ||
174 | } | 186 | } |
175 | 187 | ||
176 | @Override | 188 | @Override |
... | @@ -214,6 +226,7 @@ public class DefaultGroup extends DefaultGroupDescription | ... | @@ -214,6 +226,7 @@ public class DefaultGroup extends DefaultGroupDescription |
214 | .add("description", super.toString()) | 226 | .add("description", super.toString()) |
215 | .add("groupid", id) | 227 | .add("groupid", id) |
216 | .add("state", state) | 228 | .add("state", state) |
229 | + .add("age", age) | ||
217 | .toString(); | 230 | .toString(); |
218 | } | 231 | } |
219 | 232 | ... | ... |
... | @@ -96,4 +96,12 @@ public interface Group extends GroupDescription { | ... | @@ -96,4 +96,12 @@ public interface Group extends GroupDescription { |
96 | * @return number of flow rules or other groups pointing to this group | 96 | * @return number of flow rules or other groups pointing to this group |
97 | */ | 97 | */ |
98 | long referenceCount(); | 98 | long referenceCount(); |
99 | + | ||
100 | + /** | ||
101 | + * Obtains the age of a group. The age reflects the number of polling rounds | ||
102 | + * the group has had a reference count of zero. | ||
103 | + * | ||
104 | + * @return the age of the group as an integer | ||
105 | + */ | ||
106 | + int age(); | ||
99 | } | 107 | } | ... | ... |
... | @@ -19,15 +19,17 @@ import com.google.common.collect.FluentIterable; | ... | @@ -19,15 +19,17 @@ import com.google.common.collect.FluentIterable; |
19 | import com.google.common.collect.ImmutableSet; | 19 | import com.google.common.collect.ImmutableSet; |
20 | import com.google.common.collect.Iterables; | 20 | import com.google.common.collect.Iterables; |
21 | import com.google.common.collect.Sets; | 21 | import com.google.common.collect.Sets; |
22 | - | ||
23 | import org.apache.felix.scr.annotations.Activate; | 22 | import org.apache.felix.scr.annotations.Activate; |
24 | import org.apache.felix.scr.annotations.Component; | 23 | import org.apache.felix.scr.annotations.Component; |
25 | import org.apache.felix.scr.annotations.Deactivate; | 24 | import org.apache.felix.scr.annotations.Deactivate; |
25 | +import org.apache.felix.scr.annotations.Modified; | ||
26 | +import org.apache.felix.scr.annotations.Property; | ||
26 | import org.apache.felix.scr.annotations.Reference; | 27 | import org.apache.felix.scr.annotations.Reference; |
27 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 28 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
28 | import org.apache.felix.scr.annotations.Service; | 29 | import org.apache.felix.scr.annotations.Service; |
29 | import org.onlab.util.KryoNamespace; | 30 | import org.onlab.util.KryoNamespace; |
30 | import org.onlab.util.NewConcurrentHashMap; | 31 | import org.onlab.util.NewConcurrentHashMap; |
32 | +import org.onosproject.cfg.ComponentConfigService; | ||
31 | import org.onosproject.cluster.ClusterService; | 33 | import org.onosproject.cluster.ClusterService; |
32 | import org.onosproject.cluster.NodeId; | 34 | import org.onosproject.cluster.NodeId; |
33 | import org.onosproject.core.DefaultGroupId; | 35 | import org.onosproject.core.DefaultGroupId; |
... | @@ -54,19 +56,21 @@ import org.onosproject.net.group.StoredGroupBucketEntry; | ... | @@ -54,19 +56,21 @@ import org.onosproject.net.group.StoredGroupBucketEntry; |
54 | import org.onosproject.net.group.StoredGroupEntry; | 56 | import org.onosproject.net.group.StoredGroupEntry; |
55 | import org.onosproject.store.AbstractStore; | 57 | import org.onosproject.store.AbstractStore; |
56 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; | 58 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; |
57 | -import org.onosproject.store.service.MultiValuedTimestamp; | ||
58 | import org.onosproject.store.serializers.KryoNamespaces; | 59 | import org.onosproject.store.serializers.KryoNamespaces; |
59 | import org.onosproject.store.service.ConsistentMap; | 60 | import org.onosproject.store.service.ConsistentMap; |
60 | import org.onosproject.store.service.MapEvent; | 61 | import org.onosproject.store.service.MapEvent; |
61 | import org.onosproject.store.service.MapEventListener; | 62 | import org.onosproject.store.service.MapEventListener; |
63 | +import org.onosproject.store.service.MultiValuedTimestamp; | ||
62 | import org.onosproject.store.service.Serializer; | 64 | import org.onosproject.store.service.Serializer; |
63 | import org.onosproject.store.service.StorageService; | 65 | import org.onosproject.store.service.StorageService; |
64 | import org.onosproject.store.service.Versioned; | 66 | import org.onosproject.store.service.Versioned; |
67 | +import org.osgi.service.component.ComponentContext; | ||
65 | import org.slf4j.Logger; | 68 | import org.slf4j.Logger; |
66 | 69 | ||
67 | import java.util.ArrayList; | 70 | import java.util.ArrayList; |
68 | import java.util.Collection; | 71 | import java.util.Collection; |
69 | import java.util.Collections; | 72 | import java.util.Collections; |
73 | +import java.util.Dictionary; | ||
70 | import java.util.HashMap; | 74 | import java.util.HashMap; |
71 | import java.util.HashSet; | 75 | import java.util.HashSet; |
72 | import java.util.Iterator; | 76 | import java.util.Iterator; |
... | @@ -75,6 +79,7 @@ import java.util.Map; | ... | @@ -75,6 +79,7 @@ import java.util.Map; |
75 | import java.util.Map.Entry; | 79 | import java.util.Map.Entry; |
76 | import java.util.Objects; | 80 | import java.util.Objects; |
77 | import java.util.Optional; | 81 | import java.util.Optional; |
82 | +import java.util.Properties; | ||
78 | import java.util.Set; | 83 | import java.util.Set; |
79 | import java.util.concurrent.ConcurrentHashMap; | 84 | import java.util.concurrent.ConcurrentHashMap; |
80 | import java.util.concurrent.ConcurrentMap; | 85 | import java.util.concurrent.ConcurrentMap; |
... | @@ -83,7 +88,9 @@ import java.util.concurrent.Executors; | ... | @@ -83,7 +88,9 @@ import java.util.concurrent.Executors; |
83 | import java.util.concurrent.atomic.AtomicInteger; | 88 | import java.util.concurrent.atomic.AtomicInteger; |
84 | import java.util.stream.Collectors; | 89 | import java.util.stream.Collectors; |
85 | 90 | ||
91 | +import static com.google.common.base.Strings.isNullOrEmpty; | ||
86 | import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; | 92 | import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; |
93 | +import static org.onlab.util.Tools.get; | ||
87 | import static org.onlab.util.Tools.groupedThreads; | 94 | import static org.onlab.util.Tools.groupedThreads; |
88 | import static org.slf4j.LoggerFactory.getLogger; | 95 | import static org.slf4j.LoggerFactory.getLogger; |
89 | 96 | ||
... | @@ -99,6 +106,9 @@ public class DistributedGroupStore | ... | @@ -99,6 +106,9 @@ public class DistributedGroupStore |
99 | 106 | ||
100 | private final Logger log = getLogger(getClass()); | 107 | private final Logger log = getLogger(getClass()); |
101 | 108 | ||
109 | + private static final boolean GARBAGE_COLLECT = false; | ||
110 | + private static final int GC_THRESH = 6; | ||
111 | + | ||
102 | private final int dummyId = 0xffffffff; | 112 | private final int dummyId = 0xffffffff; |
103 | private final GroupId dummyGroupId = new DefaultGroupId(dummyId); | 113 | private final GroupId dummyGroupId = new DefaultGroupId(dummyId); |
104 | 114 | ||
... | @@ -114,6 +124,9 @@ public class DistributedGroupStore | ... | @@ -114,6 +124,9 @@ public class DistributedGroupStore |
114 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 124 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
115 | protected MastershipService mastershipService; | 125 | protected MastershipService mastershipService; |
116 | 126 | ||
127 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
128 | + protected ComponentConfigService cfgService; | ||
129 | + | ||
117 | // Per device group table with (device id + app cookie) as key | 130 | // Per device group table with (device id + app cookie) as key |
118 | private ConsistentMap<GroupStoreKeyMapKey, | 131 | private ConsistentMap<GroupStoreKeyMapKey, |
119 | StoredGroupEntry> groupStoreEntriesByKey = null; | 132 | StoredGroupEntry> groupStoreEntriesByKey = null; |
... | @@ -135,8 +148,18 @@ public class DistributedGroupStore | ... | @@ -135,8 +148,18 @@ public class DistributedGroupStore |
135 | 148 | ||
136 | private KryoNamespace clusterMsgSerializer; | 149 | private KryoNamespace clusterMsgSerializer; |
137 | 150 | ||
151 | + @Property(name = "garbageCollect", boolValue = GARBAGE_COLLECT, | ||
152 | + label = "Enable group garbage collection") | ||
153 | + private boolean garbageCollect = GARBAGE_COLLECT; | ||
154 | + | ||
155 | + @Property(name = "gcThresh", intValue = GC_THRESH, | ||
156 | + label = "Number of rounds for group garbage collection") | ||
157 | + private int gcThresh = GC_THRESH; | ||
158 | + | ||
159 | + | ||
138 | @Activate | 160 | @Activate |
139 | public void activate() { | 161 | public void activate() { |
162 | + cfgService.registerProperties(getClass()); | ||
140 | kryoBuilder = new KryoNamespace.Builder() | 163 | kryoBuilder = new KryoNamespace.Builder() |
141 | .register(KryoNamespaces.API) | 164 | .register(KryoNamespaces.API) |
142 | .register(DefaultGroup.class, | 165 | .register(DefaultGroup.class, |
... | @@ -193,12 +216,29 @@ public class DistributedGroupStore | ... | @@ -193,12 +216,29 @@ public class DistributedGroupStore |
193 | 216 | ||
194 | @Deactivate | 217 | @Deactivate |
195 | public void deactivate() { | 218 | public void deactivate() { |
219 | + cfgService.unregisterProperties(getClass(), false); | ||
196 | clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST); | 220 | clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST); |
197 | groupStoreEntriesByKey.destroy(); | 221 | groupStoreEntriesByKey.destroy(); |
198 | auditPendingReqQueue.destroy(); | 222 | auditPendingReqQueue.destroy(); |
199 | log.info("Stopped"); | 223 | log.info("Stopped"); |
200 | } | 224 | } |
201 | 225 | ||
226 | + @Modified | ||
227 | + public void modified(ComponentContext context) { | ||
228 | + Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties(); | ||
229 | + | ||
230 | + try { | ||
231 | + String s = get(properties, "garbageCollect"); | ||
232 | + garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT : Boolean.parseBoolean(s.trim()); | ||
233 | + | ||
234 | + s = get(properties, "gcThresh"); | ||
235 | + gcThresh = isNullOrEmpty(s) ? GC_THRESH : Integer.parseInt(s.trim()); | ||
236 | + } catch (Exception e) { | ||
237 | + gcThresh = GC_THRESH; | ||
238 | + garbageCollect = GARBAGE_COLLECT; | ||
239 | + } | ||
240 | + } | ||
241 | + | ||
202 | private static NewConcurrentHashMap<GroupId, Group> | 242 | private static NewConcurrentHashMap<GroupId, Group> |
203 | lazyEmptyExtraneousGroupIdTable() { | 243 | lazyEmptyExtraneousGroupIdTable() { |
204 | return NewConcurrentHashMap.<GroupId, Group>ifNeeded(); | 244 | return NewConcurrentHashMap.<GroupId, Group>ifNeeded(); |
... | @@ -268,7 +308,6 @@ public class DistributedGroupStore | ... | @@ -268,7 +308,6 @@ public class DistributedGroupStore |
268 | * Returns the groups associated with a device. | 308 | * Returns the groups associated with a device. |
269 | * | 309 | * |
270 | * @param deviceId the device ID | 310 | * @param deviceId the device ID |
271 | - * | ||
272 | * @return the group entries | 311 | * @return the group entries |
273 | */ | 312 | */ |
274 | @Override | 313 | @Override |
... | @@ -296,7 +335,6 @@ public class DistributedGroupStore | ... | @@ -296,7 +335,6 @@ public class DistributedGroupStore |
296 | * | 335 | * |
297 | * @param deviceId the device ID | 336 | * @param deviceId the device ID |
298 | * @param appCookie the group key | 337 | * @param appCookie the group key |
299 | - * | ||
300 | * @return a group associated with the key | 338 | * @return a group associated with the key |
301 | */ | 339 | */ |
302 | @Override | 340 | @Override |
... | @@ -377,7 +415,8 @@ public class DistributedGroupStore | ... | @@ -377,7 +415,8 @@ public class DistributedGroupStore |
377 | clusterCommunicator.unicast(groupOp, | 415 | clusterCommunicator.unicast(groupOp, |
378 | GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST, | 416 | GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST, |
379 | clusterMsgSerializer::serialize, | 417 | clusterMsgSerializer::serialize, |
380 | - mastershipService.getMasterFor(groupDesc.deviceId())).whenComplete((result, error) -> { | 418 | + mastershipService.getMasterFor(groupDesc.deviceId())) |
419 | + .whenComplete((result, error) -> { | ||
381 | if (error != null) { | 420 | if (error != null) { |
382 | log.warn("Failed to send request to master: {} to {}", | 421 | log.warn("Failed to send request to master: {} to {}", |
383 | groupOp, | 422 | groupOp, |
... | @@ -415,7 +454,7 @@ public class DistributedGroupStore | ... | @@ -415,7 +454,7 @@ public class DistributedGroupStore |
415 | return null; | 454 | return null; |
416 | } | 455 | } |
417 | 456 | ||
418 | - for (Group extraneousGroup:extraneousMap.values()) { | 457 | + for (Group extraneousGroup : extraneousMap.values()) { |
419 | if (extraneousGroup.buckets().equals(buckets)) { | 458 | if (extraneousGroup.buckets().equals(buckets)) { |
420 | return extraneousGroup; | 459 | return extraneousGroup; |
421 | } | 460 | } |
... | @@ -688,7 +727,7 @@ public class DistributedGroupStore | ... | @@ -688,7 +727,7 @@ public class DistributedGroupStore |
688 | if (type == UpdateType.ADD) { | 727 | if (type == UpdateType.ADD) { |
689 | // Check if the any of the new buckets are part of | 728 | // Check if the any of the new buckets are part of |
690 | // the old bucket list | 729 | // the old bucket list |
691 | - for (GroupBucket addBucket:buckets.buckets()) { | 730 | + for (GroupBucket addBucket : buckets.buckets()) { |
692 | if (!newBucketList.contains(addBucket)) { | 731 | if (!newBucketList.contains(addBucket)) { |
693 | newBucketList.add(addBucket); | 732 | newBucketList.add(addBucket); |
694 | groupDescUpdated = true; | 733 | groupDescUpdated = true; |
... | @@ -697,7 +736,7 @@ public class DistributedGroupStore | ... | @@ -697,7 +736,7 @@ public class DistributedGroupStore |
697 | } else if (type == UpdateType.REMOVE) { | 736 | } else if (type == UpdateType.REMOVE) { |
698 | // Check if the to be removed buckets are part of the | 737 | // Check if the to be removed buckets are part of the |
699 | // old bucket list | 738 | // old bucket list |
700 | - for (GroupBucket removeBucket:buckets.buckets()) { | 739 | + for (GroupBucket removeBucket : buckets.buckets()) { |
701 | if (newBucketList.contains(removeBucket)) { | 740 | if (newBucketList.contains(removeBucket)) { |
702 | newBucketList.remove(removeBucket); | 741 | newBucketList.remove(removeBucket); |
703 | groupDescUpdated = true; | 742 | groupDescUpdated = true; |
... | @@ -795,11 +834,11 @@ public class DistributedGroupStore | ... | @@ -795,11 +834,11 @@ public class DistributedGroupStore |
795 | group.id(), | 834 | group.id(), |
796 | group.deviceId()); | 835 | group.deviceId()); |
797 | synchronized (existing) { | 836 | synchronized (existing) { |
798 | - for (GroupBucket bucket:group.buckets().buckets()) { | 837 | + for (GroupBucket bucket : group.buckets().buckets()) { |
799 | Optional<GroupBucket> matchingBucket = | 838 | Optional<GroupBucket> matchingBucket = |
800 | existing.buckets().buckets() | 839 | existing.buckets().buckets() |
801 | .stream() | 840 | .stream() |
802 | - .filter((existingBucket)->(existingBucket.equals(bucket))) | 841 | + .filter((existingBucket) -> (existingBucket.equals(bucket))) |
803 | .findFirst(); | 842 | .findFirst(); |
804 | if (matchingBucket.isPresent()) { | 843 | if (matchingBucket.isPresent()) { |
805 | ((StoredGroupBucketEntry) matchingBucket. | 844 | ((StoredGroupBucketEntry) matchingBucket. |
... | @@ -814,6 +853,7 @@ public class DistributedGroupStore | ... | @@ -814,6 +853,7 @@ public class DistributedGroupStore |
814 | existing.setLife(group.life()); | 853 | existing.setLife(group.life()); |
815 | existing.setPackets(group.packets()); | 854 | existing.setPackets(group.packets()); |
816 | existing.setBytes(group.bytes()); | 855 | existing.setBytes(group.bytes()); |
856 | + existing.setReferenceCount(group.referenceCount()); | ||
817 | if ((existing.state() == GroupState.PENDING_ADD) || | 857 | if ((existing.state() == GroupState.PENDING_ADD) || |
818 | (existing.state() == GroupState.PENDING_ADD_RETRY)) { | 858 | (existing.state() == GroupState.PENDING_ADD_RETRY)) { |
819 | log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED", | 859 | log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED", |
... | @@ -901,12 +941,12 @@ public class DistributedGroupStore | ... | @@ -901,12 +941,12 @@ public class DistributedGroupStore |
901 | List<StoredGroupEntry> pendingGroupRequests = | 941 | List<StoredGroupEntry> pendingGroupRequests = |
902 | getPendingGroupKeyTable().values() | 942 | getPendingGroupKeyTable().values() |
903 | .stream() | 943 | .stream() |
904 | - .filter(g-> g.deviceId().equals(deviceId)) | 944 | + .filter(g -> g.deviceId().equals(deviceId)) |
905 | .collect(Collectors.toList()); | 945 | .collect(Collectors.toList()); |
906 | log.debug("processing pending group add requests for device {} and number of pending requests {}", | 946 | log.debug("processing pending group add requests for device {} and number of pending requests {}", |
907 | deviceId, | 947 | deviceId, |
908 | pendingGroupRequests.size()); | 948 | pendingGroupRequests.size()); |
909 | - for (Group group:pendingGroupRequests) { | 949 | + for (Group group : pendingGroupRequests) { |
910 | GroupDescription tmp = new DefaultGroupDescription( | 950 | GroupDescription tmp = new DefaultGroupDescription( |
911 | group.deviceId(), | 951 | group.deviceId(), |
912 | group.type(), | 952 | group.type(), |
... | @@ -1144,6 +1184,7 @@ public class DistributedGroupStore | ... | @@ -1144,6 +1184,7 @@ public class DistributedGroupStore |
1144 | 1184 | ||
1145 | protected static class GroupStoreKeyMapKey extends GroupStoreMapKey { | 1185 | protected static class GroupStoreKeyMapKey extends GroupStoreMapKey { |
1146 | private final GroupKey appCookie; | 1186 | private final GroupKey appCookie; |
1187 | + | ||
1147 | public GroupStoreKeyMapKey(DeviceId deviceId, | 1188 | public GroupStoreKeyMapKey(DeviceId deviceId, |
1148 | GroupKey appCookie) { | 1189 | GroupKey appCookie) { |
1149 | super(deviceId); | 1190 | super(deviceId); |
... | @@ -1175,6 +1216,7 @@ public class DistributedGroupStore | ... | @@ -1175,6 +1216,7 @@ public class DistributedGroupStore |
1175 | 1216 | ||
1176 | protected static class GroupStoreIdMapKey extends GroupStoreMapKey { | 1217 | protected static class GroupStoreIdMapKey extends GroupStoreMapKey { |
1177 | private final GroupId groupId; | 1218 | private final GroupId groupId; |
1219 | + | ||
1178 | public GroupStoreIdMapKey(DeviceId deviceId, | 1220 | public GroupStoreIdMapKey(DeviceId deviceId, |
1179 | GroupId groupId) { | 1221 | GroupId groupId) { |
1180 | super(deviceId); | 1222 | super(deviceId); |
... | @@ -1233,12 +1275,15 @@ public class DistributedGroupStore | ... | @@ -1233,12 +1275,15 @@ public class DistributedGroupStore |
1233 | log.trace("Stored Group {} for device {}", group, deviceId); | 1275 | log.trace("Stored Group {} for device {}", group, deviceId); |
1234 | } | 1276 | } |
1235 | 1277 | ||
1278 | + garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries); | ||
1279 | + | ||
1236 | for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) { | 1280 | for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) { |
1237 | Group group = it2.next(); | 1281 | Group group = it2.next(); |
1238 | if (storedGroupEntries.remove(group)) { | 1282 | if (storedGroupEntries.remove(group)) { |
1239 | // we both have the group, let's update some info then. | 1283 | // we both have the group, let's update some info then. |
1240 | log.trace("Group AUDIT: group {} exists in both planes for device {}", | 1284 | log.trace("Group AUDIT: group {} exists in both planes for device {}", |
1241 | group.id(), deviceId); | 1285 | group.id(), deviceId); |
1286 | + | ||
1242 | groupAdded(group); | 1287 | groupAdded(group); |
1243 | it2.remove(); | 1288 | it2.remove(); |
1244 | } | 1289 | } |
... | @@ -1283,6 +1328,29 @@ public class DistributedGroupStore | ... | @@ -1283,6 +1328,29 @@ public class DistributedGroupStore |
1283 | } | 1328 | } |
1284 | } | 1329 | } |
1285 | 1330 | ||
1331 | + private void garbageCollect(DeviceId deviceId, | ||
1332 | + Set<Group> southboundGroupEntries, | ||
1333 | + Set<StoredGroupEntry> storedGroupEntries) { | ||
1334 | + if (!garbageCollect) { | ||
1335 | + return; | ||
1336 | + } | ||
1337 | + | ||
1338 | + Iterator<StoredGroupEntry> it = storedGroupEntries.iterator(); | ||
1339 | + while (it.hasNext()) { | ||
1340 | + StoredGroupEntry group = it.next(); | ||
1341 | + if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) { | ||
1342 | + log.debug("Garbage collecting group {} on {}", group, deviceId); | ||
1343 | + deleteGroupDescription(deviceId, group.appCookie()); | ||
1344 | + southboundGroupEntries.remove(group); | ||
1345 | + it.remove(); | ||
1346 | + } | ||
1347 | + } | ||
1348 | + } | ||
1349 | + | ||
1350 | + private boolean checkGroupRefCount(Group group) { | ||
1351 | + return (group.referenceCount() == 0 && group.age() >= gcThresh); | ||
1352 | + } | ||
1353 | + | ||
1286 | private void groupMissing(Group group) { | 1354 | private void groupMissing(Group group) { |
1287 | switch (group.state()) { | 1355 | switch (group.state()) { |
1288 | case PENDING_DELETE: | 1356 | case PENDING_DELETE: | ... | ... |
... | @@ -23,6 +23,7 @@ import org.junit.After; | ... | @@ -23,6 +23,7 @@ import org.junit.After; |
23 | import org.junit.Before; | 23 | import org.junit.Before; |
24 | import org.junit.Test; | 24 | import org.junit.Test; |
25 | import org.onlab.junit.TestUtils; | 25 | import org.onlab.junit.TestUtils; |
26 | +import org.onosproject.cfg.ComponentConfigAdapter; | ||
26 | import org.onosproject.cluster.NodeId; | 27 | import org.onosproject.cluster.NodeId; |
27 | import org.onosproject.core.DefaultGroupId; | 28 | import org.onosproject.core.DefaultGroupId; |
28 | import org.onosproject.core.GroupId; | 29 | import org.onosproject.core.GroupId; |
... | @@ -129,6 +130,7 @@ public class DistributedGroupStoreTest { | ... | @@ -129,6 +130,7 @@ public class DistributedGroupStoreTest { |
129 | groupStoreImpl.storageService = new TestStorageService(); | 130 | groupStoreImpl.storageService = new TestStorageService(); |
130 | groupStoreImpl.clusterCommunicator = new ClusterCommunicationServiceAdapter(); | 131 | groupStoreImpl.clusterCommunicator = new ClusterCommunicationServiceAdapter(); |
131 | groupStoreImpl.mastershipService = new MasterOfAll(); | 132 | groupStoreImpl.mastershipService = new MasterOfAll(); |
133 | + groupStoreImpl.cfgService = new ComponentConfigAdapter(); | ||
132 | groupStoreImpl.activate(); | 134 | groupStoreImpl.activate(); |
133 | groupStore = groupStoreImpl; | 135 | groupStore = groupStoreImpl; |
134 | auditPendingReqQueue = | 136 | auditPendingReqQueue = | ... | ... |
... | @@ -156,6 +156,11 @@ public class GroupsResourceTest extends ResourceTest { | ... | @@ -156,6 +156,11 @@ public class GroupsResourceTest extends ResourceTest { |
156 | } | 156 | } |
157 | 157 | ||
158 | @Override | 158 | @Override |
159 | + public int age() { | ||
160 | + return 0; | ||
161 | + } | ||
162 | + | ||
163 | + @Override | ||
159 | public Type type() { | 164 | public Type type() { |
160 | return GroupDescription.Type.ALL; | 165 | return GroupDescription.Type.ALL; |
161 | } | 166 | } | ... | ... |
-
Please register or login to post a comment