blob: 87355d6402dcea6137ddc3b56da6554c16d8f808 [file] [log] [blame]
alshabib10580802015-02-18 18:30:33 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
alshabib10580802015-02-18 18:30:33 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.store.group.impl;
17
Jonathan Hart6ec029a2015-03-24 17:12:35 -070018import com.google.common.collect.FluentIterable;
Charles Chanf4838a72015-12-07 18:13:45 -080019import com.google.common.collect.ImmutableSet;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070020import com.google.common.collect.Iterables;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070021import com.google.common.collect.Sets;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070022import org.onlab.util.KryoNamespace;
alshabibb0285992016-03-28 23:30:37 -070023import org.onosproject.cfg.ComponentConfigService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070024import org.onosproject.cluster.ClusterService;
Charles Chanf4838a72015-12-07 18:13:45 -080025import org.onosproject.cluster.NodeId;
alshabib10580802015-02-18 18:30:33 -080026import org.onosproject.core.GroupId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070027import org.onosproject.mastership.MastershipService;
alshabib10580802015-02-18 18:30:33 -080028import org.onosproject.net.DeviceId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070029import org.onosproject.net.MastershipRole;
jaegonkim9477a9d2018-04-01 16:36:36 +090030import org.onosproject.net.driver.DriverService;
alshabib10580802015-02-18 18:30:33 -080031import org.onosproject.net.group.DefaultGroup;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070032import org.onosproject.net.group.DefaultGroupBucket;
alshabib10580802015-02-18 18:30:33 -080033import org.onosproject.net.group.DefaultGroupDescription;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070034import org.onosproject.net.group.DefaultGroupKey;
alshabib10580802015-02-18 18:30:33 -080035import org.onosproject.net.group.Group;
36import org.onosproject.net.group.Group.GroupState;
37import org.onosproject.net.group.GroupBucket;
38import org.onosproject.net.group.GroupBuckets;
39import org.onosproject.net.group.GroupDescription;
40import org.onosproject.net.group.GroupEvent;
41import org.onosproject.net.group.GroupEvent.Type;
42import org.onosproject.net.group.GroupKey;
43import org.onosproject.net.group.GroupOperation;
44import org.onosproject.net.group.GroupStore;
45import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070046import org.onosproject.net.group.StoredGroupBucketEntry;
alshabib10580802015-02-18 18:30:33 -080047import org.onosproject.net.group.StoredGroupEntry;
48import org.onosproject.store.AbstractStore;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070049import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070050import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani0b847532016-03-03 13:44:15 -080051import org.onosproject.store.service.ConsistentMap;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070052import org.onosproject.store.service.DistributedPrimitive.Status;
Madan Jampani0b847532016-03-03 13:44:15 -080053import org.onosproject.store.service.MapEvent;
54import org.onosproject.store.service.MapEventListener;
alshabibb0285992016-03-28 23:30:37 -070055import org.onosproject.store.service.MultiValuedTimestamp;
Madan Jampani0b847532016-03-03 13:44:15 -080056import org.onosproject.store.service.Serializer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070057import org.onosproject.store.service.StorageService;
helenyrwua1c41152016-08-18 16:16:14 -070058import org.onosproject.store.service.Topic;
Madan Jampani0b847532016-03-03 13:44:15 -080059import org.onosproject.store.service.Versioned;
alshabibb0285992016-03-28 23:30:37 -070060import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070061import org.osgi.service.component.annotations.Activate;
62import org.osgi.service.component.annotations.Component;
63import org.osgi.service.component.annotations.Deactivate;
64import org.osgi.service.component.annotations.Modified;
65import org.osgi.service.component.annotations.Reference;
66import org.osgi.service.component.annotations.ReferenceCardinality;
alshabib10580802015-02-18 18:30:33 -080067import org.slf4j.Logger;
68
Jonathan Hart6ec029a2015-03-24 17:12:35 -070069import java.util.ArrayList;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070070import java.util.Collection;
Charles Chanf4838a72015-12-07 18:13:45 -080071import java.util.Collections;
alshabibb0285992016-03-28 23:30:37 -070072import java.util.Dictionary;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070073import java.util.HashMap;
Charles Chan0c7c43b2016-01-14 17:39:20 -080074import java.util.HashSet;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070075import java.util.Iterator;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070076import java.util.List;
Madan Jampani0b847532016-03-03 13:44:15 -080077import java.util.Map;
Charles Chan0c7c43b2016-01-14 17:39:20 -080078import java.util.Map.Entry;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070079import java.util.Objects;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070080import java.util.Optional;
alshabibb0285992016-03-28 23:30:37 -070081import java.util.Properties;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070082import java.util.Set;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070083import java.util.concurrent.ConcurrentHashMap;
84import java.util.concurrent.ConcurrentMap;
85import java.util.concurrent.ExecutorService;
86import java.util.concurrent.Executors;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053087import java.util.concurrent.ScheduledExecutorService;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070088import java.util.concurrent.atomic.AtomicInteger;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053089import java.util.function.Consumer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070090import java.util.stream.Collectors;
91
alshabibb0285992016-03-28 23:30:37 -070092import static com.google.common.base.Strings.isNullOrEmpty;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053093import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
alshabibb0285992016-03-28 23:30:37 -070094import static org.onlab.util.Tools.get;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070095import static org.onlab.util.Tools.groupedThreads;
Ray Milkeyb5646e62018-10-16 11:42:18 -070096import static org.onosproject.store.OsgiPropertyConstants.ALLOW_EXTRANEOUS_GROUPS;
97import static org.onosproject.store.OsgiPropertyConstants.ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
98import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT;
99import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_DEFAULT;
100import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_THRESH;
101import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_THRESH_DEFAULT;
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700102import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -0800103
104/**
Saurav Das0fd79d92016-03-07 10:58:36 -0800105 * Manages inventory of group entries using distributed group stores from the
106 * storage service.
alshabib10580802015-02-18 18:30:33 -0800107 */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700108@Component(
109 immediate = true,
110 service = GroupStore.class,
111 property = {
Ray Milkey2d7bca12018-10-17 14:51:52 -0700112 GARBAGE_COLLECT + ":Boolean=" + GARBAGE_COLLECT_DEFAULT,
113 GARBAGE_COLLECT_THRESH + ":Integer=" + GARBAGE_COLLECT_THRESH_DEFAULT,
114 ALLOW_EXTRANEOUS_GROUPS + ":Boolean=" + ALLOW_EXTRANEOUS_GROUPS_DEFAULT
Ray Milkeyb5646e62018-10-16 11:42:18 -0700115 }
116)
alshabib10580802015-02-18 18:30:33 -0800117public class DistributedGroupStore
118 extends AbstractStore<GroupEvent, GroupStoreDelegate>
119 implements GroupStore {
120
121 private final Logger log = getLogger(getClass());
122
Saurav Das137f27f2018-06-11 17:02:31 -0700123 private static final int MAX_FAILED_ATTEMPTS = 3;
alshabibb0285992016-03-28 23:30:37 -0700124
alshabib10580802015-02-18 18:30:33 -0800125 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -0800126 private final GroupId dummyGroupId = new GroupId(dummyId);
alshabib10580802015-02-18 18:30:33 -0800127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700129 protected ClusterCommunicationService clusterCommunicator;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700132 protected ClusterService clusterService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700135 protected StorageService storageService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700138 protected MastershipService mastershipService;
139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibb0285992016-03-28 23:30:37 -0700141 protected ComponentConfigService cfgService;
142
jaegonkim9477a9d2018-04-01 16:36:36 +0900143 // Guarantees enabling DriverService before enabling GroupStore
144 // (DriverService is used in serializing/de-serializing DefaultGroup)
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim9477a9d2018-04-01 16:36:36 +0900146 protected DriverService driverService;
147
pier00ac83e2019-12-19 16:04:23 +0100148 private NodeId local;
149
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530150 private ScheduledExecutorService executor;
151 private Consumer<Status> statusChangeListener;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700152 // Per device group table with (device id + app cookie) as key
Madan Jampani0b847532016-03-03 13:44:15 -0800153 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700154 StoredGroupEntry> groupStoreEntriesByKey = null;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700155 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700156 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
alshabibb0285992016-03-28 23:30:37 -0700157 groupEntriesById = new ConcurrentHashMap<>();
Madan Jampani0b847532016-03-03 13:44:15 -0800158 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700159 StoredGroupEntry> auditPendingReqQueue = null;
Frank Wange0eb5ce2016-07-01 18:21:25 +0800160 private MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry>
161 mapListener = new GroupStoreKeyMapListener();
alshabib10580802015-02-18 18:30:33 -0800162 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
163 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700164 private ExecutorService messageHandlingExecutor;
165 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
Saurav Das137f27f2018-06-11 17:02:31 -0700166
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700167 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
alshabib10580802015-02-18 18:30:33 -0800168
169 private final AtomicInteger groupIdGen = new AtomicInteger();
170
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700171 private KryoNamespace clusterMsgSerializer;
172
helenyrwua1c41152016-08-18 16:16:14 -0700173 private static Topic<GroupStoreMessage> groupTopic;
174
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700175 /** Enable group garbage collection. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700176 private boolean garbageCollect = GARBAGE_COLLECT_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700177
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700178 /** Number of rounds for group garbage collection. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700179 private int gcThresh = GARBAGE_COLLECT_THRESH_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700180
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700181 /** Allow groups in switches not installed by ONOS. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700182 private boolean allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700183
alshabib10580802015-02-18 18:30:33 -0800184 @Activate
sisubram4beea652017-08-09 10:38:14 +0000185 public void activate(ComponentContext context) {
alshabibb0285992016-03-28 23:30:37 -0700186 cfgService.registerProperties(getClass());
sisubram4beea652017-08-09 10:38:14 +0000187 modified(context);
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700188 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700189 .register(KryoNamespaces.API)
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700190 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
alshabibb0285992016-03-28 23:30:37 -0700191 .register(DefaultGroup.class,
192 DefaultGroupBucket.class,
193 DefaultGroupDescription.class,
194 DefaultGroupKey.class,
195 GroupDescription.Type.class,
196 Group.GroupState.class,
197 GroupBuckets.class,
alshabibb0285992016-03-28 23:30:37 -0700198 GroupStoreMessage.class,
199 GroupStoreMessage.Type.class,
200 UpdateType.class,
201 GroupStoreMessageSubjects.class,
202 MultiValuedTimestamp.class,
203 GroupStoreKeyMapKey.class,
204 GroupStoreIdMapKey.class,
205 GroupStoreMapKey.class
206 );
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700207
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700208 clusterMsgSerializer = kryoBuilder.build("GroupStore");
209 Serializer serializer = Serializer.using(clusterMsgSerializer);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700210
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700211 messageHandlingExecutor = Executors.
212 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
213 groupedThreads("onos/store/group",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700214 "message-handlers",
215 log));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700216
217 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700218 clusterMsgSerializer::deserialize,
219 this::process,
220 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700221
Madan Jampani0b847532016-03-03 13:44:15 -0800222 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700223
Madan Jampani0b847532016-03-03 13:44:15 -0800224 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
225 .withName("onos-group-store-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700226 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700227 .build();
Frank Wange0eb5ce2016-07-01 18:21:25 +0800228 groupStoreEntriesByKey.addListener(mapListener);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700229 log.debug("Current size of groupstorekeymap:{}",
230 groupStoreEntriesByKey.size());
Thiago Santosfb73c502016-08-18 18:15:13 -0300231 synchronizeGroupStoreEntries();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700232
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530233 log.debug("Creating GroupStoreId Map From GroupStoreKey Map");
234 matchGroupEntries();
235 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/group", "store", log));
236 statusChangeListener = status -> {
237 if (status == Status.ACTIVE) {
238 executor.execute(this::matchGroupEntries);
239 }
240 };
241 groupStoreEntriesByKey.addStatusChangeListener(statusChangeListener);
242
Madan Jampani0b847532016-03-03 13:44:15 -0800243 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700244
Madan Jampani0b847532016-03-03 13:44:15 -0800245 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
246 .withName("onos-pending-group-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700247 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700248 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700249 log.debug("Current size of pendinggroupkeymap:{}",
250 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700251
helenyrwua1c41152016-08-18 16:16:14 -0700252 groupTopic = getOrCreateGroupTopic(serializer);
253 groupTopic.subscribe(this::processGroupMessage);
254
pier00ac83e2019-12-19 16:04:23 +0100255 local = clusterService.getLocalNode().id();
256
alshabib10580802015-02-18 18:30:33 -0800257 log.info("Started");
258 }
259
260 @Deactivate
261 public void deactivate() {
Frank Wange0eb5ce2016-07-01 18:21:25 +0800262 groupStoreEntriesByKey.removeListener(mapListener);
alshabibb0285992016-03-28 23:30:37 -0700263 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700264 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
alshabib10580802015-02-18 18:30:33 -0800265 log.info("Stopped");
266 }
267
alshabibb0285992016-03-28 23:30:37 -0700268 @Modified
269 public void modified(ComponentContext context) {
270 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
271
272 try {
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700273 String s = get(properties, GARBAGE_COLLECT);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700274 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT_DEFAULT : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700275
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700276 s = get(properties, GARBAGE_COLLECT_THRESH);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700277 gcThresh = isNullOrEmpty(s) ? GARBAGE_COLLECT_THRESH_DEFAULT : Integer.parseInt(s.trim());
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530278
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700279 s = get(properties, ALLOW_EXTRANEOUS_GROUPS);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700280 allowExtraneousGroups = isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_GROUPS_DEFAULT : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700281 } catch (Exception e) {
Ray Milkeyb5646e62018-10-16 11:42:18 -0700282 gcThresh = GARBAGE_COLLECT_THRESH_DEFAULT;
283 garbageCollect = GARBAGE_COLLECT_DEFAULT;
284 allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700285 }
286 }
287
helenyrwua1c41152016-08-18 16:16:14 -0700288 private Topic<GroupStoreMessage> getOrCreateGroupTopic(Serializer serializer) {
289 if (groupTopic == null) {
290 return storageService.getTopic("group-failover-notif", serializer);
291 } else {
292 return groupTopic;
293 }
Sho SHIMIZUa6285542017-01-12 15:08:24 -0800294 }
helenyrwua1c41152016-08-18 16:16:14 -0700295
alshabib10580802015-02-18 18:30:33 -0800296 /**
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530297 * Updating values of groupEntriesById.
298 */
299 private void matchGroupEntries() {
300 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupStoreEntriesByKey.asJavaMap().entrySet()) {
301 StoredGroupEntry group = entry.getValue();
302 getGroupIdTable(entry.getKey().deviceId()).put(group.id(), group);
303 }
304 }
305
Thiago Santosfb73c502016-08-18 18:15:13 -0300306
307 private void synchronizeGroupStoreEntries() {
308 Map<GroupStoreKeyMapKey, StoredGroupEntry> groupEntryMap = groupStoreEntriesByKey.asJavaMap();
309 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupEntryMap.entrySet()) {
Thiago Santosfb73c502016-08-18 18:15:13 -0300310 StoredGroupEntry value = entry.getValue();
Thiago Santosfb73c502016-08-18 18:15:13 -0300311 ConcurrentMap<GroupId, StoredGroupEntry> groupIdTable = getGroupIdTable(value.deviceId());
312 groupIdTable.put(value.id(), value);
313 }
314 }
315
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530316 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700317 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800318 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700319 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800320 */
Madan Jampani0b847532016-03-03 13:44:15 -0800321 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700322 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800323 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800324 }
325
326 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700327 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800328 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700329 * @param deviceId identifier of the device
330 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800331 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700332 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700333 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800334 }
335
336 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700337 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800338 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700339 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800340 */
Madan Jampani0b847532016-03-03 13:44:15 -0800341 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700342 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800343 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800344 }
345
346 /**
347 * Returns the extraneous group id table for specified device.
348 *
349 * @param deviceId identifier of the device
350 * @return Map representing group key table of given device.
351 */
352 private ConcurrentMap<GroupId, Group>
353 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700354 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800355 }
356
357 /**
358 * Returns the number of groups for the specified device in the store.
359 *
360 * @return number of groups for the specified device
361 */
362 @Override
363 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700364 return (getGroups(deviceId) != null) ?
alshabibb0285992016-03-28 23:30:37 -0700365 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800366 }
367
368 /**
369 * Returns the groups associated with a device.
370 *
371 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800372 * @return the group entries
373 */
374 @Override
375 public Iterable<Group> getGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800376 // Let ImmutableSet.copyOf do the type conversion
377 return ImmutableSet.copyOf(getStoredGroups(deviceId));
alshabib10580802015-02-18 18:30:33 -0800378 }
379
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700380 private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800381 NodeId master = mastershipService.getMasterFor(deviceId);
382 if (master == null) {
383 log.debug("Failed to getGroups: No master for {}", deviceId);
384 return Collections.emptySet();
385 }
386
387 Set<StoredGroupEntry> storedGroups = getGroupStoreKeyMap().values()
388 .stream()
389 .filter(input -> input.deviceId().equals(deviceId))
390 .collect(Collectors.toSet());
391 return ImmutableSet.copyOf(storedGroups);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700392 }
393
alshabib10580802015-02-18 18:30:33 -0800394 /**
395 * Returns the stored group entry.
396 *
alshabibb0285992016-03-28 23:30:37 -0700397 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800398 * @param appCookie the group key
alshabib10580802015-02-18 18:30:33 -0800399 * @return a group associated with the key
400 */
401 @Override
402 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700403 return getStoredGroupEntry(deviceId, appCookie);
404 }
405
406 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
407 GroupKey appCookie) {
408 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
409 appCookie));
410 }
411
412 @Override
413 public Group getGroup(DeviceId deviceId, GroupId groupId) {
414 return getStoredGroupEntry(deviceId, groupId);
415 }
416
417 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
418 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700419 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800420 }
421
422 private int getFreeGroupIdValue(DeviceId deviceId) {
423 int freeId = groupIdGen.incrementAndGet();
424
425 while (true) {
Yi Tsengfa394de2017-02-01 11:26:40 -0800426 Group existing = getGroup(deviceId, new GroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800427 if (existing == null) {
428 existing = (
429 extraneousGroupEntriesById.get(deviceId) != null) ?
430 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800431 get(new GroupId(freeId)) :
alshabib10580802015-02-18 18:30:33 -0800432 null;
433 }
434 if (existing != null) {
435 freeId = groupIdGen.incrementAndGet();
436 } else {
437 break;
438 }
439 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700440 log.debug("getFreeGroupIdValue: Next Free ID is {}", freeId);
alshabib10580802015-02-18 18:30:33 -0800441 return freeId;
442 }
443
444 /**
445 * Stores a new group entry using the information from group description.
446 *
447 * @param groupDesc group description to be used to create group entry
448 */
449 @Override
450 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700451 log.debug("In storeGroupDescription");
alshabib10580802015-02-18 18:30:33 -0800452 // Check if a group is existing with the same key
Saurav Das8a0732e2015-11-20 15:27:53 -0800453 Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
454 if (existingGroup != null) {
Saurav Dasc568c342018-01-25 09:49:01 -0800455 log.debug("Group already exists with the same key {} in dev:{} with id:0x{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800456 groupDesc.appCookie(), groupDesc.deviceId(),
457 Integer.toHexString(existingGroup.id().id()));
alshabib10580802015-02-18 18:30:33 -0800458 return;
459 }
460
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700461 // Check if group to be created by a remote instance
Madan Jampani175e8fd2015-05-20 14:10:45 -0700462 if (mastershipService.getLocalRole(groupDesc.deviceId()) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700463 log.debug("storeGroupDescription: Device {} local role is not MASTER",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700464 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700465 if (mastershipService.getMasterFor(groupDesc.deviceId()) == null) {
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530466 log.debug("No Master for device {}..."
467 + "Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700468 groupDesc.deviceId());
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530469 addToPendingAudit(groupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700470 return;
471 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700472 GroupStoreMessage groupOp = GroupStoreMessage.
473 createGroupAddRequestMsg(groupDesc.deviceId(),
474 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700475
Madan Jampani175e8fd2015-05-20 14:10:45 -0700476 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700477 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
478 clusterMsgSerializer::serialize,
479 mastershipService.getMasterFor(groupDesc.deviceId()))
480 .whenComplete((result, error) -> {
Madan Jampani175e8fd2015-05-20 14:10:45 -0700481 if (error != null) {
482 log.warn("Failed to send request to master: {} to {}",
alshabibb0285992016-03-28 23:30:37 -0700483 groupOp,
484 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700485 //TODO: Send Group operation failure event
486 } else {
487 log.debug("Sent Group operation request for device {} "
alshabibb0285992016-03-28 23:30:37 -0700488 + "to remote MASTER {}",
489 groupDesc.deviceId(),
490 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700491 }
492 });
alshabib10580802015-02-18 18:30:33 -0800493 return;
494 }
495
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700496 log.debug("Store group for device {} is getting handled locally",
497 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800498 storeGroupDescriptionInternal(groupDesc);
499 }
500
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530501 private void addToPendingAudit(GroupDescription groupDesc) {
502 Integer groupIdVal = groupDesc.givenGroupId();
503 GroupId groupId = (groupIdVal != null) ? new GroupId(groupIdVal) : dummyGroupId;
504 addToPendingKeyTable(new DefaultGroup(groupId, groupDesc));
505 }
506
507 private void addToPendingKeyTable(StoredGroupEntry group) {
508 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
509 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
510 getPendingGroupKeyTable();
511 pendingKeyTable.put(new GroupStoreKeyMapKey(group.deviceId(),
512 group.appCookie()),
513 group);
514 }
515
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700516 private Group getMatchingExtraneousGroupbyId(DeviceId deviceId, Integer groupId) {
517 ConcurrentMap<GroupId, Group> extraneousMap =
518 extraneousGroupEntriesById.get(deviceId);
519 if (extraneousMap == null) {
520 return null;
521 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800522 return extraneousMap.get(new GroupId(groupId));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700523 }
524
525 private Group getMatchingExtraneousGroupbyBuckets(DeviceId deviceId,
526 GroupBuckets buckets) {
527 ConcurrentMap<GroupId, Group> extraneousMap =
528 extraneousGroupEntriesById.get(deviceId);
529 if (extraneousMap == null) {
530 return null;
531 }
532
alshabibb0285992016-03-28 23:30:37 -0700533 for (Group extraneousGroup : extraneousMap.values()) {
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700534 if (extraneousGroup.buckets().equals(buckets)) {
535 return extraneousGroup;
536 }
537 }
538 return null;
539 }
540
alshabib10580802015-02-18 18:30:33 -0800541 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
542 // Check if a group is existing with the same key
543 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
544 return;
545 }
546
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700547 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
548 // Device group audit has not completed yet
549 // Add this group description to pending group key table
550 // Create a group entry object with Dummy Group ID
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700551 log.debug("storeGroupDescriptionInternal: Device {} AUDIT pending...Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700552 groupDesc.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700553 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
554 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
Madan Jampani0b847532016-03-03 13:44:15 -0800555 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700556 getPendingGroupKeyTable();
557 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
558 groupDesc.appCookie()),
559 group);
560 return;
561 }
562
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700563 Group matchingExtraneousGroup = null;
564 if (groupDesc.givenGroupId() != null) {
565 //Check if there is a extraneous group existing with the same Id
566 matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
alshabibb0285992016-03-28 23:30:37 -0700567 groupDesc.deviceId(), groupDesc.givenGroupId());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700568 if (matchingExtraneousGroup != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800569 log.debug("storeGroupDescriptionInternal: Matching extraneous group "
alshabibb0285992016-03-28 23:30:37 -0700570 + "found in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700571 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800572 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700573 //Check if the group buckets matches with user provided buckets
574 if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
575 //Group is already existing with the same buckets and Id
576 // Create a group entry object
Saurav Das0fd79d92016-03-07 10:58:36 -0800577 log.debug("storeGroupDescriptionInternal: Buckets also matching "
alshabibb0285992016-03-28 23:30:37 -0700578 + "in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700579 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800580 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700581 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700582 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700583 // Insert the newly created group entry into key and id maps
584 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700585 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
586 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700587 // Ensure it also inserted into group id based table to
588 // avoid any chances of duplication in group id generation
589 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700590 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700591 addOrUpdateGroupEntry(matchingExtraneousGroup);
592 removeExtraneousGroupEntry(matchingExtraneousGroup);
593 return;
594 } else {
595 //Group buckets are not matching. Update group
596 //with user provided buckets.
Saurav Das0fd79d92016-03-07 10:58:36 -0800597 log.debug("storeGroupDescriptionInternal: Buckets are not "
alshabibb0285992016-03-28 23:30:37 -0700598 + "matching in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700599 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800600 Integer.toHexString(groupDesc.givenGroupId()));
601 StoredGroupEntry modifiedGroup = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700602 matchingExtraneousGroup.id(), groupDesc);
Saurav Das0fd79d92016-03-07 10:58:36 -0800603 modifiedGroup.setState(GroupState.PENDING_UPDATE);
604 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700605 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
606 groupDesc.appCookie()), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800607 // Ensure it also inserted into group id based table to
608 // avoid any chances of duplication in group id generation
609 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700610 put(matchingExtraneousGroup.id(), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800611 removeExtraneousGroupEntry(matchingExtraneousGroup);
612 log.debug("storeGroupDescriptionInternal: Triggering Group "
alshabibb0285992016-03-28 23:30:37 -0700613 + "UPDATE request for {} in device {}",
Saurav Das0fd79d92016-03-07 10:58:36 -0800614 matchingExtraneousGroup.id(),
615 groupDesc.deviceId());
616 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
617 return;
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700618 }
619 }
620 } else {
621 //Check if there is an extraneous group with user provided buckets
622 matchingExtraneousGroup = getMatchingExtraneousGroupbyBuckets(
alshabibb0285992016-03-28 23:30:37 -0700623 groupDesc.deviceId(), groupDesc.buckets());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700624 if (matchingExtraneousGroup != null) {
625 //Group is already existing with the same buckets.
626 //So reuse this group.
627 log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {}",
628 groupDesc.deviceId());
629 //Create a group entry object
630 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700631 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700632 // Insert the newly created group entry into key and id maps
633 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700634 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
635 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700636 // Ensure it also inserted into group id based table to
637 // avoid any chances of duplication in group id generation
638 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700639 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700640 addOrUpdateGroupEntry(matchingExtraneousGroup);
641 removeExtraneousGroupEntry(matchingExtraneousGroup);
642 return;
643 } else {
644 //TODO: Check if there are any empty groups that can be used here
645 log.debug("storeGroupDescriptionInternal: No matching extraneous groups found in Device {}",
646 groupDesc.deviceId());
647 }
648 }
649
Saurav Das100e3b82015-04-30 11:12:10 -0700650 GroupId id = null;
651 if (groupDesc.givenGroupId() == null) {
652 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800653 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700654 } else {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800655 // we need to use the identifier passed in by caller, but check if
656 // already used
657 Group existing = getGroup(groupDesc.deviceId(),
Yi Tsengfa394de2017-02-01 11:26:40 -0800658 new GroupId(groupDesc.givenGroupId()));
Saurav Das8be4e3a2016-03-11 17:19:07 -0800659 if (existing != null) {
660 log.warn("Group already exists with the same id: 0x{} in dev:{} "
alshabibb0285992016-03-28 23:30:37 -0700661 + "but with different key: {} (request gkey: {})",
662 Integer.toHexString(groupDesc.givenGroupId()),
663 groupDesc.deviceId(),
664 existing.appCookie(),
665 groupDesc.appCookie());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800666 return;
667 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800668 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700669 }
alshabib10580802015-02-18 18:30:33 -0800670 // Create a group entry object
671 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700672 // Insert the newly created group entry into key and id maps
673 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700674 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
675 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700676 // Ensure it also inserted into group id based table to
677 // avoid any chances of duplication in group id generation
678 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700679 put(id, group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700680 log.debug("storeGroupDescriptionInternal: Processing Group ADD request for Id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700681 id,
682 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800683 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
684 group));
685 }
686
687 /**
688 * Updates the existing group entry with the information
689 * from group description.
690 *
alshabibb0285992016-03-28 23:30:37 -0700691 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800692 * @param oldAppCookie the current group key
alshabibb0285992016-03-28 23:30:37 -0700693 * @param type update type
694 * @param newBuckets group buckets for updates
alshabib10580802015-02-18 18:30:33 -0800695 * @param newAppCookie optional new group key
696 */
697 @Override
698 public void updateGroupDescription(DeviceId deviceId,
699 GroupKey oldAppCookie,
700 UpdateType type,
701 GroupBuckets newBuckets,
702 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700703 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700704 if (mastershipService.getMasterFor(deviceId) != null &&
705 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700706 log.debug("updateGroupDescription: Device {} local role is not MASTER",
707 deviceId);
708 if (mastershipService.getMasterFor(deviceId) == null) {
709 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700710 + "Can not perform update group operation",
711 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700712 //TODO: Send Group operation failure event
713 return;
714 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700715 GroupStoreMessage groupOp = GroupStoreMessage.
716 createGroupUpdateRequestMsg(deviceId,
717 oldAppCookie,
718 type,
719 newBuckets,
720 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700721
Madan Jampani175e8fd2015-05-20 14:10:45 -0700722 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700723 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
724 clusterMsgSerializer::serialize,
725 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
726 if (error != null) {
727 log.warn("Failed to send request to master: {} to {}",
728 groupOp,
729 mastershipService.getMasterFor(deviceId), error);
730 }
731 //TODO: Send Group operation failure event
732 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700733 return;
734 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700735 log.debug("updateGroupDescription for device {} is getting handled locally",
736 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700737 updateGroupDescriptionInternal(deviceId,
738 oldAppCookie,
739 type,
740 newBuckets,
741 newAppCookie);
742 }
743
744 private void updateGroupDescriptionInternal(DeviceId deviceId,
alshabibb0285992016-03-28 23:30:37 -0700745 GroupKey oldAppCookie,
746 UpdateType type,
747 GroupBuckets newBuckets,
748 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800749 // Check if a group is existing with the provided key
750 Group oldGroup = getGroup(deviceId, oldAppCookie);
751 if (oldGroup == null) {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800752 log.warn("updateGroupDescriptionInternal: Group not found...strange. "
alshabibb0285992016-03-28 23:30:37 -0700753 + "GroupKey:{} DeviceId:{}", oldAppCookie, deviceId);
alshabib10580802015-02-18 18:30:33 -0800754 return;
755 }
756
757 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
758 type,
759 newBuckets);
760 if (newBucketList != null) {
761 // Create a new group object from the old group
762 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
763 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
764 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
765 oldGroup.deviceId(),
766 oldGroup.type(),
767 updatedBuckets,
768 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700769 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800770 oldGroup.appId());
771 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
772 updatedGroupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700773 log.debug("updateGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_UPDATE",
alshabibb0285992016-03-28 23:30:37 -0700774 oldGroup.id(),
775 oldGroup.deviceId(),
776 oldGroup.state());
alshabib10580802015-02-18 18:30:33 -0800777 newGroup.setState(GroupState.PENDING_UPDATE);
778 newGroup.setLife(oldGroup.life());
779 newGroup.setPackets(oldGroup.packets());
780 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700781 //Update the group entry in groupkey based map.
782 //Update to groupid based map will happen in the
783 //groupkey based map update listener
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700784 log.debug("updateGroupDescriptionInternal with type {}: Group updated with buckets",
785 type);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700786 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700787 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
788 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800789 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700790 } else {
791 log.warn("updateGroupDescriptionInternal with type {}: No "
alshabibb0285992016-03-28 23:30:37 -0700792 + "change in the buckets in update", type);
alshabib10580802015-02-18 18:30:33 -0800793 }
794 }
795
796 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
797 UpdateType type,
798 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300799 if (type == UpdateType.SET) {
800 return buckets.buckets();
801 }
802
Victor Silvadf1eeae2016-08-12 15:28:57 -0300803 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
804 List<GroupBucket> updatedBucketList = new ArrayList<>();
alshabib10580802015-02-18 18:30:33 -0800805 boolean groupDescUpdated = false;
806
807 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300808 List<GroupBucket> newBuckets = buckets.buckets();
809
810 // Add old buckets that will not be updated and check if any will be updated.
811 for (GroupBucket oldBucket : oldBuckets) {
812 int newBucketIndex = newBuckets.indexOf(oldBucket);
813
814 if (newBucketIndex != -1) {
815 GroupBucket newBucket = newBuckets.get(newBucketIndex);
816 if (!newBucket.hasSameParameters(oldBucket)) {
817 // Bucket will be updated
818 groupDescUpdated = true;
819 }
820 } else {
821 // Old bucket will remain the same - add it.
822 updatedBucketList.add(oldBucket);
alshabib10580802015-02-18 18:30:33 -0800823 }
824 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300825
826 // Add all new buckets
827 updatedBucketList.addAll(newBuckets);
828 if (!oldBuckets.containsAll(newBuckets)) {
829 groupDescUpdated = true;
830 }
831
alshabib10580802015-02-18 18:30:33 -0800832 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300833 List<GroupBucket> bucketsToRemove = buckets.buckets();
834
835 // Check which old buckets should remain
836 for (GroupBucket oldBucket : oldBuckets) {
837 if (!bucketsToRemove.contains(oldBucket)) {
838 updatedBucketList.add(oldBucket);
839 } else {
alshabib10580802015-02-18 18:30:33 -0800840 groupDescUpdated = true;
841 }
842 }
843 }
844
845 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300846 return updatedBucketList;
alshabib10580802015-02-18 18:30:33 -0800847 } else {
848 return null;
849 }
850 }
851
852 /**
853 * Triggers deleting the existing group entry.
854 *
alshabibb0285992016-03-28 23:30:37 -0700855 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800856 * @param appCookie the group key
857 */
858 @Override
859 public void deleteGroupDescription(DeviceId deviceId,
860 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700861 // Check if group to be deleted by a remote instance
862 if (mastershipService.
863 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700864 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
865 deviceId);
866 if (mastershipService.getMasterFor(deviceId) == null) {
867 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700868 + "Can not perform delete group operation",
869 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700870 //TODO: Send Group operation failure event
871 return;
872 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700873 GroupStoreMessage groupOp = GroupStoreMessage.
874 createGroupDeleteRequestMsg(deviceId,
875 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700876
Madan Jampani175e8fd2015-05-20 14:10:45 -0700877 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700878 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
879 clusterMsgSerializer::serialize,
880 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
881 if (error != null) {
882 log.warn("Failed to send request to master: {} to {}",
883 groupOp,
884 mastershipService.getMasterFor(deviceId), error);
885 }
886 //TODO: Send Group operation failure event
887 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700888 return;
889 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700890 log.debug("deleteGroupDescription in device {} is getting handled locally",
891 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700892 deleteGroupDescriptionInternal(deviceId, appCookie);
893 }
894
895 private void deleteGroupDescriptionInternal(DeviceId deviceId,
896 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800897 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700898 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800899 if (existing == null) {
900 return;
901 }
902
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700903 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700904 existing.id(),
905 existing.deviceId(),
906 existing.state());
alshabib10580802015-02-18 18:30:33 -0800907 synchronized (existing) {
908 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700909 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700910 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
911 existing);
alshabib10580802015-02-18 18:30:33 -0800912 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700913 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED",
914 deviceId);
alshabib10580802015-02-18 18:30:33 -0800915 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
916 }
917
918 /**
919 * Stores a new group entry, or updates an existing entry.
920 *
921 * @param group group entry
922 */
923 @Override
924 public void addOrUpdateGroupEntry(Group group) {
925 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700926 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
927 group.id());
alshabib10580802015-02-18 18:30:33 -0800928 GroupEvent event = null;
929
930 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800931 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700932 group.id(),
933 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800934 synchronized (existing) {
alshabibb0285992016-03-28 23:30:37 -0700935 for (GroupBucket bucket : group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700936 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700937 existing.buckets().buckets()
alshabibb0285992016-03-28 23:30:37 -0700938 .stream()
939 .filter((existingBucket) -> (existingBucket.equals(bucket)))
940 .findFirst();
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700941 if (matchingBucket.isPresent()) {
942 ((StoredGroupBucketEntry) matchingBucket.
943 get()).setPackets(bucket.packets());
944 ((StoredGroupBucketEntry) matchingBucket.
945 get()).setBytes(bucket.bytes());
946 } else {
947 log.warn("addOrUpdateGroupEntry: No matching "
alshabibb0285992016-03-28 23:30:37 -0700948 + "buckets to update stats");
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700949 }
950 }
alshabib10580802015-02-18 18:30:33 -0800951 existing.setLife(group.life());
952 existing.setPackets(group.packets());
953 existing.setBytes(group.bytes());
alshabibb0285992016-03-28 23:30:37 -0700954 existing.setReferenceCount(group.referenceCount());
Saurav Das137f27f2018-06-11 17:02:31 -0700955 existing.setFailedRetryCount(0);
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700956 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700957 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800958 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700959 existing.id(),
960 existing.deviceId(),
961 existing.state());
alshabib10580802015-02-18 18:30:33 -0800962 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700963 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800964 event = new GroupEvent(Type.GROUP_ADDED, existing);
965 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800966 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700967 existing.id(),
968 existing.deviceId(),
969 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700970 existing.setState(GroupState.ADDED);
971 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800972 event = new GroupEvent(Type.GROUP_UPDATED, existing);
973 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700974 //Re-PUT map entries to trigger map update events
975 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700976 put(new GroupStoreKeyMapKey(existing.deviceId(),
977 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800978 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700979 } else {
980 log.warn("addOrUpdateGroupEntry: Group update "
alshabibb0285992016-03-28 23:30:37 -0700981 + "happening for a non-existing entry in the map");
alshabib10580802015-02-18 18:30:33 -0800982 }
983
Saurav Das137f27f2018-06-11 17:02:31 -0700984 // XXX if map is going to trigger event, is this one needed?
alshabib10580802015-02-18 18:30:33 -0800985 if (event != null) {
986 notifyDelegate(event);
987 }
988 }
989
990 /**
991 * Removes the group entry from store.
992 *
993 * @param group group entry
994 */
995 @Override
996 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700997 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
998 group.id());
alshabib10580802015-02-18 18:30:33 -0800999
1000 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001001 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001002 group.id(),
1003 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001004 //Removal from groupid based map will happen in the
1005 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001006 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1007 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001008 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001009 } else {
1010 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -07001011 + "not existing in our maps",
1012 group.id(),
1013 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001014 }
1015 }
1016
Victor Silva4e8b7832016-08-17 17:11:19 -03001017 private void purgeGroupEntries(Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entries) {
1018 entries.forEach(entry -> {
1019 groupStoreEntriesByKey.remove(entry.getKey());
1020 });
1021 }
1022
alshabib10580802015-02-18 18:30:33 -08001023 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -08001024 public void purgeGroupEntry(DeviceId deviceId) {
Victor Silva4e8b7832016-08-17 17:11:19 -03001025 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entriesPendingRemove =
Charles Chan0c7c43b2016-01-14 17:39:20 -08001026 new HashSet<>();
1027
Madan Jampani0b847532016-03-03 13:44:15 -08001028 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -08001029 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Victor Silva4e8b7832016-08-17 17:11:19 -03001030 .forEach(entriesPendingRemove::add);
Charles Chan0c7c43b2016-01-14 17:39:20 -08001031
Victor Silva4e8b7832016-08-17 17:11:19 -03001032 purgeGroupEntries(entriesPendingRemove);
1033 }
1034
1035 @Override
1036 public void purgeGroupEntries() {
1037 purgeGroupEntries(getGroupStoreKeyMap().entrySet());
Charles Chan0c7c43b2016-01-14 17:39:20 -08001038 }
1039
1040 @Override
alshabib10580802015-02-18 18:30:33 -08001041 public void deviceInitialAuditCompleted(DeviceId deviceId,
1042 boolean completed) {
1043 synchronized (deviceAuditStatus) {
1044 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001045 log.debug("AUDIT completed for device {}",
1046 deviceId);
alshabib10580802015-02-18 18:30:33 -08001047 deviceAuditStatus.put(deviceId, true);
1048 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001049 List<StoredGroupEntry> pendingGroupRequests =
1050 getPendingGroupKeyTable().values()
alshabibb0285992016-03-28 23:30:37 -07001051 .stream()
1052 .filter(g -> g.deviceId().equals(deviceId))
1053 .collect(Collectors.toList());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001054 log.debug("processing pending group add requests for device {} and number of pending requests {}",
alshabibb0285992016-03-28 23:30:37 -07001055 deviceId,
1056 pendingGroupRequests.size());
1057 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -08001058 GroupDescription tmp = new DefaultGroupDescription(
1059 group.deviceId(),
1060 group.type(),
1061 group.buckets(),
1062 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -07001063 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -08001064 group.appId());
1065 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001066 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -07001067 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001068 }
alshabib10580802015-02-18 18:30:33 -08001069 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001070 Boolean audited = deviceAuditStatus.get(deviceId);
1071 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001072 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -08001073 deviceAuditStatus.put(deviceId, false);
1074 }
1075 }
1076 }
1077 }
1078
1079 @Override
1080 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
1081 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001082 Boolean audited = deviceAuditStatus.get(deviceId);
1083 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -08001084 }
1085 }
1086
1087 @Override
1088 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
1089
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001090 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
1091 operation.groupId());
alshabib10580802015-02-18 18:30:33 -08001092
1093 if (existing == null) {
1094 log.warn("No group entry with ID {} found ", operation.groupId());
1095 return;
1096 }
1097
Saurav Das137f27f2018-06-11 17:02:31 -07001098 log.warn("groupOperationFailed: group operation {} failed in state {} "
alshabibb0285992016-03-28 23:30:37 -07001099 + "for group {} in device {} with code {}",
1100 operation.opType(),
Saurav Das137f27f2018-06-11 17:02:31 -07001101 existing.state(),
alshabibb0285992016-03-28 23:30:37 -07001102 existing.id(),
1103 existing.deviceId(),
1104 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -08001105 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001106 if (operation.buckets().equals(existing.buckets())) {
Saurav Dasc88d4662017-05-15 15:34:25 -07001107 if (existing.state() == GroupState.PENDING_ADD ||
1108 existing.state() == GroupState.PENDING_ADD_RETRY) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001109 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001110 + "add state - moving to ADDED for group {} in device {}",
1111 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001112 addOrUpdateGroupEntry(existing);
1113 return;
1114 } else {
Saurav Dasc88d4662017-05-15 15:34:25 -07001115 log.warn("GROUP_EXISTS: GroupId and Buckets match but existing"
1116 + "group in state: {}", existing.state());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001117 }
Saurav Dasc88d4662017-05-15 15:34:25 -07001118 } else {
1119 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
1120 + "Operation: {} Existing: {}", operation.buckets(),
1121 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001122 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001123 }
Saurav Das137f27f2018-06-11 17:02:31 -07001124 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.INVALID_GROUP) {
1125 existing.incrFailedRetryCount();
1126 if (existing.failedRetryCount() < MAX_FAILED_ATTEMPTS) {
1127 log.warn("Group {} programming failed {} of {} times in dev {}, "
1128 + "retrying ..", existing.id(),
1129 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1130 deviceId);
1131 return;
1132 }
1133 log.warn("Group {} programming failed {} of {} times in dev {}, "
1134 + "removing group from store", existing.id(),
1135 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1136 deviceId);
1137 // fall through to case
1138 }
1139
alshabib10580802015-02-18 18:30:33 -08001140 switch (operation.opType()) {
1141 case ADD:
Saurav Das137f27f2018-06-11 17:02:31 -07001142 if (existing.state() == GroupState.PENDING_ADD
1143 || existing.state() == GroupState.PENDING_ADD_RETRY) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001144 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
1145 log.warn("groupOperationFailed: cleaningup "
alshabibb0285992016-03-28 23:30:37 -07001146 + "group {} from store in device {}....",
1147 existing.id(),
1148 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001149 //Removal from groupid based map will happen in the
1150 //map update listener
1151 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1152 existing.appCookie()));
1153 }
alshabib10580802015-02-18 18:30:33 -08001154 break;
1155 case MODIFY:
1156 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1157 break;
1158 case DELETE:
1159 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1160 break;
1161 default:
1162 log.warn("Unknown group operation type {}", operation.opType());
1163 }
alshabib10580802015-02-18 18:30:33 -08001164 }
1165
1166 @Override
1167 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001168 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001169 group.id(),
1170 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001171 ConcurrentMap<GroupId, Group> extraneousIdTable =
1172 getExtraneousGroupIdTable(group.deviceId());
1173 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001174 // Don't remove the extraneous groups, instead re-use it when
1175 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001176 }
1177
1178 @Override
1179 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001180 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001181 group.id(),
1182 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001183 ConcurrentMap<GroupId, Group> extraneousIdTable =
1184 getExtraneousGroupIdTable(group.deviceId());
1185 extraneousIdTable.remove(group.id());
1186 }
1187
1188 @Override
1189 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1190 // flatten and make iterator unmodifiable
1191 return FluentIterable.from(
1192 getExtraneousGroupIdTable(deviceId).values());
1193 }
1194
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001195 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001196 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001197 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001198 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001199 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001200
1201 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001202 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001203 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001204 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001205 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001206 if ((key == null) && (group == null)) {
1207 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001208 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001209 return;
1210 } else if (group == null) {
1211 group = getGroupIdTable(key.deviceId()).values()
1212 .stream()
1213 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
Yuta HIGUCHI6e5f4702016-11-21 11:42:11 -08001214 .findFirst().orElse(null);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001215 if (group == null) {
1216 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001217 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001218 return;
1219 }
1220 }
1221 log.trace("received groupid map event {} for id {} in device {}",
1222 mapEvent.type(),
1223 group.id(),
jaegonkim68e080c2016-12-01 22:31:01 +09001224 (key != null ? key.deviceId() : null));
Madan Jampani0b847532016-03-03 13:44:15 -08001225 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001226 // Update the group ID table
1227 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001228 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1229 if (value.state() == Group.GroupState.ADDED) {
1230 if (value.isGroupStateAddedFirstTime()) {
1231 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001232 log.trace("Received first time GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001233 group.id(),
1234 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001235 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001236 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001237 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001238 group.id(),
1239 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001240 }
1241 }
Madan Jampani0b847532016-03-03 13:44:15 -08001242 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001243 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001244 // Remove the entry from the group ID table
1245 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001246 }
1247
1248 if (groupEvent != null) {
1249 notifyDelegate(groupEvent);
1250 }
1251 }
1252 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001253
helenyrwua1c41152016-08-18 16:16:14 -07001254 private void processGroupMessage(GroupStoreMessage message) {
1255 if (message.type() == GroupStoreMessage.Type.FAILOVER) {
1256 // FIXME: groupStoreEntriesByKey inaccessible here
1257 getGroupIdTable(message.deviceId()).values()
1258 .stream()
1259 .filter((storedGroup) -> (storedGroup.appCookie().equals(message.appCookie())))
1260 .findFirst().ifPresent(group -> notifyDelegate(new GroupEvent(Type.GROUP_BUCKET_FAILOVER, group)));
1261 }
1262 }
1263
Madan Jampani01e05fb2015-08-13 13:29:36 -07001264 private void process(GroupStoreMessage groupOp) {
1265 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001266 groupOp.type(),
1267 groupOp.deviceId());
1268 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
1269 log.warn("This node is not MASTER for device {}", groupOp.deviceId());
1270 return;
1271 }
1272 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1273 storeGroupDescriptionInternal(groupOp.groupDesc());
1274 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1275 updateGroupDescriptionInternal(groupOp.deviceId(),
1276 groupOp.appCookie(),
1277 groupOp.updateType(),
1278 groupOp.updateBuckets(),
1279 groupOp.newAppCookie());
1280 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1281 deleteGroupDescriptionInternal(groupOp.deviceId(),
1282 groupOp.appCookie());
1283 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001284 }
1285
1286 /**
1287 * Flattened map key to be used to store group entries.
1288 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001289 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001290 private final DeviceId deviceId;
1291
1292 public GroupStoreMapKey(DeviceId deviceId) {
1293 this.deviceId = deviceId;
1294 }
1295
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001296 public DeviceId deviceId() {
1297 return deviceId;
1298 }
1299
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001300 @Override
1301 public boolean equals(Object o) {
1302 if (this == o) {
1303 return true;
1304 }
1305 if (!(o instanceof GroupStoreMapKey)) {
1306 return false;
1307 }
1308 GroupStoreMapKey that = (GroupStoreMapKey) o;
1309 return this.deviceId.equals(that.deviceId);
1310 }
1311
1312 @Override
1313 public int hashCode() {
1314 int result = 17;
1315
1316 result = 31 * result + Objects.hash(this.deviceId);
1317
1318 return result;
1319 }
1320 }
1321
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001322 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001323 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001324
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001325 public GroupStoreKeyMapKey(DeviceId deviceId,
1326 GroupKey appCookie) {
1327 super(deviceId);
1328 this.appCookie = appCookie;
1329 }
1330
1331 @Override
1332 public boolean equals(Object o) {
1333 if (this == o) {
1334 return true;
1335 }
1336 if (!(o instanceof GroupStoreKeyMapKey)) {
1337 return false;
1338 }
1339 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1340 return (super.equals(that) &&
1341 this.appCookie.equals(that.appCookie));
1342 }
1343
1344 @Override
1345 public int hashCode() {
1346 int result = 17;
1347
1348 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1349
1350 return result;
1351 }
1352 }
1353
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001354 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001355 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001356
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001357 public GroupStoreIdMapKey(DeviceId deviceId,
1358 GroupId groupId) {
1359 super(deviceId);
1360 this.groupId = groupId;
1361 }
1362
1363 @Override
1364 public boolean equals(Object o) {
1365 if (this == o) {
1366 return true;
1367 }
1368 if (!(o instanceof GroupStoreIdMapKey)) {
1369 return false;
1370 }
1371 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1372 return (super.equals(that) &&
1373 this.groupId.equals(that.groupId));
1374 }
1375
1376 @Override
1377 public int hashCode() {
1378 int result = 17;
1379
1380 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1381
1382 return result;
1383 }
1384 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001385
1386 @Override
1387 public void pushGroupMetrics(DeviceId deviceId,
1388 Collection<Group> groupEntries) {
1389 boolean deviceInitialAuditStatus =
1390 deviceInitialAuditStatus(deviceId);
1391 Set<Group> southboundGroupEntries =
1392 Sets.newHashSet(groupEntries);
1393 Set<StoredGroupEntry> storedGroupEntries =
1394 Sets.newHashSet(getStoredGroups(deviceId));
1395 Set<Group> extraneousStoredEntries =
1396 Sets.newHashSet(getExtraneousGroups(deviceId));
pier00ac83e2019-12-19 16:04:23 +01001397 NodeId master;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001398
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001399 if (log.isTraceEnabled()) {
1400 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1401 southboundGroupEntries.size(),
1402 deviceId);
1403 for (Group group : southboundGroupEntries) {
1404 log.trace("Group {} in device {}", group, deviceId);
1405 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001406
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001407 log.trace("Displaying all ({}) stored group entries for device {}",
1408 storedGroupEntries.size(),
1409 deviceId);
1410 for (StoredGroupEntry group : storedGroupEntries) {
1411 log.trace("Stored Group {} for device {}", group, deviceId);
1412 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001413 }
1414
alshabibb0285992016-03-28 23:30:37 -07001415 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1416
pier00ac83e2019-12-19 16:04:23 +01001417 // update stats
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001418 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
pier00ac83e2019-12-19 16:04:23 +01001419 // Mastership change can occur during this iteration
1420 master = mastershipService.getMasterFor(deviceId);
1421 if (!Objects.equals(local, master)) {
1422 log.warn("Tried to update the group stats while the node was not the master");
1423 return;
1424 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001425 Group group = it2.next();
1426 if (storedGroupEntries.remove(group)) {
1427 // we both have the group, let's update some info then.
1428 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001429 group.id(), deviceId);
1430
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001431 groupAdded(group);
1432 it2.remove();
1433 }
1434 }
pier00ac83e2019-12-19 16:04:23 +01001435
1436 // extraneous groups in the dataplane
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001437 for (Group group : southboundGroupEntries) {
1438 if (getGroup(group.deviceId(), group.id()) != null) {
1439 // There is a group existing with the same id
1440 // It is possible that group update is
1441 // in progress while we got a stale info from switch
1442 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001443 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001444 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001445 + "Group exists in ID based table while "
1446 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001447 }
1448 } else {
pier00ac83e2019-12-19 16:04:23 +01001449 // Mastership change can occur during this iteration
1450 master = mastershipService.getMasterFor(deviceId);
1451 if (!Objects.equals(local, master)) {
1452 log.warn("Tried to process extraneous groups while the node was not the master");
1453 return;
1454 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001455 // there are groups in the switch that aren't in the store
1456 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001457 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001458 extraneousStoredEntries.remove(group);
Kavitha Alagesanc56cded2017-01-13 10:48:18 +05301459 if (allowExtraneousGroups) {
1460 extraneousGroup(group);
1461 } else {
1462 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
1463 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001464 }
1465 }
pier00ac83e2019-12-19 16:04:23 +01001466
1467 // missing groups in the dataplane
Charles Chan07f15f22018-05-08 21:35:50 -07001468 for (StoredGroupEntry group : storedGroupEntries) {
pier00ac83e2019-12-19 16:04:23 +01001469 // Mastership change can occur during this iteration
1470 master = mastershipService.getMasterFor(deviceId);
1471 if (!Objects.equals(local, master)) {
1472 log.warn("Tried to process missing groups while the node was not the master");
1473 return;
1474 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001475 // there are groups in the store that aren't in the switch
1476 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001477 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001478 groupMissing(group);
1479 }
pier00ac83e2019-12-19 16:04:23 +01001480
1481 // extraneous groups in the store
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001482 for (Group group : extraneousStoredEntries) {
pier00ac83e2019-12-19 16:04:23 +01001483 // Mastership change can occur during this iteration
1484 master = mastershipService.getMasterFor(deviceId);
1485 if (!Objects.equals(local, master)) {
1486 log.warn("Tried to process node extraneous groups while the node was not the master");
1487 return;
1488 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001489 // there are groups in the extraneous store that
1490 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001491 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001492 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001493 removeExtraneousGroupEntry(group);
1494 }
1495
1496 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001497 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001498 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001499 deviceInitialAuditCompleted(deviceId, true);
1500 }
1501 }
1502
helenyrwu89470f12016-08-12 13:18:10 -07001503 @Override
1504 public void notifyOfFailovers(Collection<Group> failoverGroups) {
helenyrwu89470f12016-08-12 13:18:10 -07001505 failoverGroups.forEach(group -> {
1506 if (group.type() == Group.Type.FAILOVER) {
helenyrwua1c41152016-08-18 16:16:14 -07001507 groupTopic.publish(GroupStoreMessage.createGroupFailoverMsg(
1508 group.deviceId(), group));
helenyrwu89470f12016-08-12 13:18:10 -07001509 }
1510 });
helenyrwu89470f12016-08-12 13:18:10 -07001511 }
1512
alshabibb0285992016-03-28 23:30:37 -07001513 private void garbageCollect(DeviceId deviceId,
1514 Set<Group> southboundGroupEntries,
1515 Set<StoredGroupEntry> storedGroupEntries) {
1516 if (!garbageCollect) {
1517 return;
1518 }
1519
pier00ac83e2019-12-19 16:04:23 +01001520 NodeId master;
alshabibb0285992016-03-28 23:30:37 -07001521 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1522 while (it.hasNext()) {
pier00ac83e2019-12-19 16:04:23 +01001523 // Mastership change can occur during this iteration
1524 master = mastershipService.getMasterFor(deviceId);
1525 if (!Objects.equals(local, master)) {
1526 log.warn("Tried to run garbage collector while the node was not the master");
1527 return;
1528 }
alshabibb0285992016-03-28 23:30:37 -07001529 StoredGroupEntry group = it.next();
1530 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1531 log.debug("Garbage collecting group {} on {}", group, deviceId);
1532 deleteGroupDescription(deviceId, group.appCookie());
1533 southboundGroupEntries.remove(group);
1534 it.remove();
1535 }
1536 }
1537 }
1538
1539 private boolean checkGroupRefCount(Group group) {
1540 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1541 }
1542
Charles Chan07f15f22018-05-08 21:35:50 -07001543 private void groupMissing(StoredGroupEntry group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001544 switch (group.state()) {
1545 case PENDING_DELETE:
1546 log.debug("Group {} delete confirmation from device {}",
1547 group, group.deviceId());
1548 removeGroupEntry(group);
1549 break;
1550 case ADDED:
1551 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001552 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001553 case PENDING_UPDATE:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001554 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
Charles Chan07f15f22018-05-08 21:35:50 -07001555 group.id(),
1556 group.deviceId(),
1557 group.state());
1558 group.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001559 //Re-PUT map entries to trigger map update events
Charles Chan07f15f22018-05-08 21:35:50 -07001560 getGroupStoreKeyMap().put(new GroupStoreKeyMapKey(group.deviceId(), group.appCookie()), group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001561 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1562 group));
1563 break;
1564 default:
1565 log.debug("Group {} has not been installed.", group);
1566 break;
1567 }
1568 }
1569
1570 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001571 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001572 group, group.deviceId());
1573 addOrUpdateExtraneousGroupEntry(group);
1574 }
1575
1576 private void groupAdded(Group group) {
1577 log.trace("Group {} Added or Updated in device {}",
1578 group, group.deviceId());
1579 addOrUpdateGroupEntry(group);
1580 }
alshabib10580802015-02-18 18:30:33 -08001581}