blob: b2c5ade724fc855bcbd0fd1965c81a8eb8066b83 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.mastership.impl;
tomb41d1ac2014-09-24 01:51:24 -070017
Brian O'Connorabafb502014-12-02 22:26:20 -080018import static org.onosproject.mastership.MastershipEvent.Type.MASTER_CHANGED;
19import static org.onosproject.mastership.MastershipEvent.Type.BACKUPS_CHANGED;
Yuta HIGUCHIf1d159a2014-10-29 23:31:40 -070020import static org.apache.commons.lang3.concurrent.ConcurrentUtils.putIfAbsent;
alshabib339a3d92014-09-26 17:54:32 -070021
HIGUCHI Yuta59f02292015-02-25 19:51:48 -080022import java.util.ArrayList;
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -070023import java.util.HashSet;
HIGUCHI Yuta59f02292015-02-25 19:51:48 -080024import java.util.List;
alshabib339a3d92014-09-26 17:54:32 -070025import java.util.Map;
HIGUCHI Yuta59f02292015-02-25 19:51:48 -080026import java.util.Map.Entry;
alshabib339a3d92014-09-26 17:54:32 -070027import java.util.Set;
Madan Jampanif7536ab2015-05-07 23:23:23 -070028import java.util.concurrent.CompletableFuture;
Yuta HIGUCHIc8e19d42014-09-24 17:20:52 -070029
tomb41d1ac2014-09-24 01:51:24 -070030import org.apache.felix.scr.annotations.Activate;
31import org.apache.felix.scr.annotations.Component;
32import org.apache.felix.scr.annotations.Deactivate;
33import org.apache.felix.scr.annotations.Reference;
34import org.apache.felix.scr.annotations.ReferenceCardinality;
35import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.cluster.ClusterService;
37import org.onosproject.cluster.NodeId;
38import org.onosproject.cluster.RoleInfo;
39import org.onosproject.mastership.MastershipEvent;
40import org.onosproject.mastership.MastershipStore;
41import org.onosproject.mastership.MastershipStoreDelegate;
42import org.onosproject.mastership.MastershipTerm;
43import org.onosproject.net.DeviceId;
44import org.onosproject.net.MastershipRole;
45import org.onosproject.store.hz.AbstractHazelcastStore;
46import org.onosproject.store.hz.SMap;
47import org.onosproject.store.serializers.KryoNamespaces;
48import org.onosproject.store.serializers.KryoSerializer;
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070049import org.onlab.util.KryoNamespace;
tomb41d1ac2014-09-24 01:51:24 -070050
Ayaka Koshibe98bd12f2014-11-01 20:13:37 -070051import com.google.common.base.Objects;
Yuta HIGUCHId36a58e2014-12-02 12:46:33 -080052import com.hazelcast.config.Config;
53import com.hazelcast.config.MapConfig;
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -070054import com.hazelcast.core.EntryEvent;
55import com.hazelcast.core.EntryListener;
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -070056import com.hazelcast.core.MapEvent;
Yuta HIGUCHI41f2ec02014-10-27 09:54:43 -070057
Brian O'Connorabafb502014-12-02 22:26:20 -080058import static org.onosproject.net.MastershipRole.*;
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -070059
tomb41d1ac2014-09-24 01:51:24 -070060/**
Ayaka Koshibec4047702014-10-07 14:43:52 -070061 * Distributed implementation of the mastership store. The store is
62 * responsible for the master selection process.
tomb41d1ac2014-09-24 01:51:24 -070063 */
Madan Jampani5756c352015-04-29 00:23:58 -070064@Component(immediate = true, enabled = false)
tomb41d1ac2014-09-24 01:51:24 -070065@Service
tom0755a362014-09-24 11:54:43 -070066public class DistributedMastershipStore
Yuta HIGUCHIb0daa152014-11-10 16:58:57 -080067 extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate>
68 implements MastershipStore {
tomb41d1ac2014-09-24 01:51:24 -070069
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -070070 //term number representing that master has never been chosen yet
71 private static final Integer NOTHING = 0;
Ayaka Koshibec4047702014-10-07 14:43:52 -070072 //initial term/TTL value
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -070073 private static final Integer INIT = 1;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070074
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -070075 //device to node roles
Yuta HIGUCHId36a58e2014-12-02 12:46:33 -080076 private static final String NODE_ROLES_MAP_NAME = "nodeRoles";
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -070077 protected SMap<DeviceId, RoleValue> roleMap;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070078 //devices to terms
Yuta HIGUCHId36a58e2014-12-02 12:46:33 -080079 private static final String TERMS_MAP_NAME = "terms";
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -070080 protected SMap<DeviceId, Integer> terms;
Ayaka Koshibe8583ff32014-10-02 16:25:30 -070081
tomb41d1ac2014-09-24 01:51:24 -070082 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected ClusterService clusterService;
84
Yuta HIGUCHId1a63e92014-12-02 13:14:28 -080085 private String listenerId;
86
Ayaka Koshibe406d0102014-09-24 16:08:12 -070087 @Override
tomb41d1ac2014-09-24 01:51:24 -070088 @Activate
89 public void activate() {
90 super.activate();
91
Ayaka Koshibee8e45352014-10-16 00:37:19 -070092 this.serializer = new KryoSerializer() {
93 @Override
94 protected void setupKryoPool() {
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070095 serializerPool = KryoNamespace.newBuilder()
96 .register(KryoNamespaces.API)
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080097 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
98 .register(new RoleValueSerializer(), RoleValue.class)
99 .build();
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700100 }
101 };
102
Yuta HIGUCHId36a58e2014-12-02 12:46:33 -0800103 final Config config = theInstance.getConfig();
104
105 MapConfig nodeRolesCfg = config.getMapConfig(NODE_ROLES_MAP_NAME);
106 nodeRolesCfg.setAsyncBackupCount(MapConfig.MAX_BACKUP_COUNT - nodeRolesCfg.getBackupCount());
107
108 MapConfig termsCfg = config.getMapConfig(TERMS_MAP_NAME);
109 termsCfg.setAsyncBackupCount(MapConfig.MAX_BACKUP_COUNT - termsCfg.getBackupCount());
110
111 roleMap = new SMap<>(theInstance.<byte[], byte[]>getMap(NODE_ROLES_MAP_NAME), this.serializer);
Yuta HIGUCHId1a63e92014-12-02 13:14:28 -0800112 listenerId = roleMap.addEntryListener((new RemoteMasterShipEventHandler()), true);
Yuta HIGUCHId36a58e2014-12-02 12:46:33 -0800113 terms = new SMap<>(theInstance.<byte[], byte[]>getMap(TERMS_MAP_NAME), this.serializer);
Yuta HIGUCHIc8e19d42014-09-24 17:20:52 -0700114
tomb41d1ac2014-09-24 01:51:24 -0700115 log.info("Started");
116 }
117
118 @Deactivate
119 public void deactivate() {
Yuta HIGUCHId1a63e92014-12-02 13:14:28 -0800120 roleMap.removeEntryListener(listenerId);
tomb41d1ac2014-09-24 01:51:24 -0700121 log.info("Stopped");
122 }
123
124 @Override
Ayaka Koshibec4047702014-10-07 14:43:52 -0700125 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700126 final RoleValue roleInfo = roleMap.get(deviceId);
127 if (roleInfo != null) {
128 return roleInfo.getRole(nodeId);
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700129 }
130 return NONE;
Ayaka Koshibec4047702014-10-07 14:43:52 -0700131 }
132
133 @Override
Madan Jampanif7536ab2015-05-07 23:23:23 -0700134 public CompletableFuture<MastershipEvent> setMaster(NodeId newMaster, DeviceId deviceId) {
tomb41d1ac2014-09-24 01:51:24 -0700135
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700136 roleMap.lock(deviceId);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700137 try {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700138 final RoleValue rv = getRoleValue(deviceId);
139 final MastershipRole currentRole = rv.getRole(newMaster);
140 switch (currentRole) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700141 case MASTER:
Ayaka Koshibec4047702014-10-07 14:43:52 -0700142 //reinforce mastership
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700143 // RoleInfo integrity check
144 boolean modified = rv.reassign(newMaster, STANDBY, NONE);
145 if (modified) {
146 roleMap.put(deviceId, rv);
147 // should never reach here.
148 log.warn("{} was in both MASTER and STANDBY for {}", newMaster, deviceId);
149 // trigger BACKUPS_CHANGED?
150 }
Madan Jampanif7536ab2015-05-07 23:23:23 -0700151 return CompletableFuture.completedFuture(null);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700152 case STANDBY:
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700153 case NONE:
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700154 final NodeId currentMaster = rv.get(MASTER);
155 if (currentMaster != null) {
156 // place current master in STANDBY
157 rv.reassign(currentMaster, NONE, STANDBY);
158 rv.replace(currentMaster, newMaster, MASTER);
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700159 } else {
160 //no master before so just add.
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700161 rv.add(MASTER, newMaster);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700162 }
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700163 // remove newMaster from STANDBY
164 rv.reassign(newMaster, STANDBY, NONE);
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700165 updateTerm(deviceId);
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700166 roleMap.put(deviceId, rv);
Madan Jampanif7536ab2015-05-07 23:23:23 -0700167 return CompletableFuture.completedFuture(
168 new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()));
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700169 default:
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700170 log.warn("unknown Mastership Role {}", currentRole);
Madan Jampanif7536ab2015-05-07 23:23:23 -0700171 return CompletableFuture.completedFuture(null);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700172 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700173 } finally {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700174 roleMap.unlock(deviceId);
tomb41d1ac2014-09-24 01:51:24 -0700175 }
176 }
177
178 @Override
179 public NodeId getMaster(DeviceId deviceId) {
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700180 return getNode(MASTER, deviceId);
tomb41d1ac2014-09-24 01:51:24 -0700181 }
182
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700183
184 @Override
Ayaka Koshibeabedb092014-10-20 17:01:31 -0700185 public RoleInfo getNodes(DeviceId deviceId) {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700186 RoleValue rv = roleMap.get(deviceId);
187 if (rv != null) {
Ayaka Koshibeabedb092014-10-20 17:01:31 -0700188 return rv.roleInfo();
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700189 } else {
190 return new RoleInfo();
Ayaka Koshibe45503ce2014-10-14 11:26:45 -0700191 }
192 }
193
tomb41d1ac2014-09-24 01:51:24 -0700194 @Override
195 public Set<DeviceId> getDevices(NodeId nodeId) {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700196 Set<DeviceId> devices = new HashSet<>();
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700197
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700198 for (Map.Entry<DeviceId, RoleValue> el : roleMap.entrySet()) {
199 if (nodeId.equals(el.getValue().get(MASTER))) {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700200 devices.add(el.getKey());
tomb41d1ac2014-09-24 01:51:24 -0700201 }
202 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700203
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700204 return devices;
tomb41d1ac2014-09-24 01:51:24 -0700205 }
206
207 @Override
Madan Jampanide003d92015-05-11 17:14:20 -0700208 public CompletableFuture<MastershipRole> requestRole(DeviceId deviceId) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700209
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700210 // if no master => become master
211 // if there already exists a master:
212 // if I was the master return MASTER
213 // else put myself in STANDBY and return STANDBY
214
215 final NodeId local = clusterService.getLocalNode().id();
216 boolean modified = false;
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700217 roleMap.lock(deviceId);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700218 try {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700219 final RoleValue rv = getRoleValue(deviceId);
220 if (rv.get(MASTER) == null) {
221 // there's no master become one
222 // move out from STANDBY
223 rv.reassign(local, STANDBY, NONE);
224 rv.add(MASTER, local);
225
226 updateTerm(deviceId);
227 roleMap.put(deviceId, rv);
Madan Jampanide003d92015-05-11 17:14:20 -0700228 return CompletableFuture.completedFuture(MASTER);
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700229 }
230 final MastershipRole currentRole = rv.getRole(local);
231 switch (currentRole) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700232 case MASTER:
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700233 // RoleInfo integrity check
234 modified = rv.reassign(local, STANDBY, NONE);
235 if (modified) {
236 log.warn("{} was in both MASTER and STANDBY for {}", local, deviceId);
237 // should never reach here,
238 // but heal if we happened to be there
239 roleMap.put(deviceId, rv);
240 // trigger BACKUPS_CHANGED?
241 }
Madan Jampanide003d92015-05-11 17:14:20 -0700242 return CompletableFuture.completedFuture(currentRole);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700243 case STANDBY:
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700244 // RoleInfo integrity check
245 modified = rv.reassign(local, NONE, STANDBY);
246 if (modified) {
247 log.warn("{} was in both NONE and STANDBY for {}", local, deviceId);
248 // should never reach here,
249 // but heal if we happened to be there
250 roleMap.put(deviceId, rv);
251 // trigger BACKUPS_CHANGED?
252 }
Madan Jampanide003d92015-05-11 17:14:20 -0700253 return CompletableFuture.completedFuture(currentRole);
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700254 case NONE:
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700255 rv.reassign(local, NONE, STANDBY);
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700256 roleMap.put(deviceId, rv);
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700257 // TODO: notifyDelegate BACKUPS_CHANGED
Madan Jampanide003d92015-05-11 17:14:20 -0700258 return CompletableFuture.completedFuture(STANDBY);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700259 default:
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700260 log.warn("unknown Mastership Role {}", currentRole);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700261 }
Madan Jampanide003d92015-05-11 17:14:20 -0700262 return CompletableFuture.completedFuture(currentRole);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700263 } finally {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700264 roleMap.unlock(deviceId);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700265 }
tomb41d1ac2014-09-24 01:51:24 -0700266 }
267
268 @Override
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700269 public MastershipTerm getTermFor(DeviceId deviceId) {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700270 // term information and role must be read atomically
271 // acquiring write lock for the device
272 roleMap.lock(deviceId);
273 try {
274 RoleValue rv = getRoleValue(deviceId);
275 final Integer term = terms.get(deviceId);
276 final NodeId master = rv.get(MASTER);
Yuta HIGUCHIdfe6e3b2014-10-30 11:31:51 -0700277 if (term == null) {
278 return MastershipTerm.of(null, NOTHING);
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700279 }
280 return MastershipTerm.of(master, term);
281 } finally {
282 roleMap.unlock(deviceId);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700283 }
Ayaka Koshibeb70d34b2014-09-25 15:43:01 -0700284 }
285
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700286 @Override
Madan Jampanif7536ab2015-05-07 23:23:23 -0700287 public CompletableFuture<MastershipEvent> setStandby(NodeId nodeId, DeviceId deviceId) {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700288 // if nodeId was MASTER, rotate STANDBY
289 // if nodeId was STANDBY no-op
290 // if nodeId was NONE, add to STANDBY
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700291
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700292 roleMap.lock(deviceId);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700293 try {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700294 final RoleValue rv = getRoleValue(deviceId);
295 final MastershipRole currentRole = getRole(nodeId, deviceId);
296 switch (currentRole) {
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700297 case MASTER:
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700298 NodeId newMaster = reelect(nodeId, deviceId, rv);
299 rv.reassign(nodeId, NONE, STANDBY);
Yuta HIGUCHI0c6e1842014-11-05 22:34:23 -0800300 updateTerm(deviceId);
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700301 if (newMaster != null) {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700302 roleMap.put(deviceId, rv);
Madan Jampanif7536ab2015-05-07 23:23:23 -0700303 return CompletableFuture.completedFuture(
304 new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()));
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700305 } else {
306 // no master candidate
307 roleMap.put(deviceId, rv);
Yuta HIGUCHI65934892014-12-04 17:47:44 -0800308 // TBD: Should there be new event type for no MASTER?
Madan Jampanif7536ab2015-05-07 23:23:23 -0700309 return CompletableFuture.completedFuture(
310 new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()));
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700311 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700312 case STANDBY:
Madan Jampanif7536ab2015-05-07 23:23:23 -0700313 return CompletableFuture.completedFuture(null);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700314 case NONE:
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700315 rv.reassign(nodeId, NONE, STANDBY);
Ayaka Koshibee8e45352014-10-16 00:37:19 -0700316 roleMap.put(deviceId, rv);
Madan Jampanif7536ab2015-05-07 23:23:23 -0700317 return CompletableFuture.completedFuture(
318 new MastershipEvent(BACKUPS_CHANGED, deviceId, rv.roleInfo()));
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700319 default:
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700320 log.warn("unknown Mastership Role {}", currentRole);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700321 }
Madan Jampanif7536ab2015-05-07 23:23:23 -0700322 return CompletableFuture.completedFuture(null);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700323 } finally {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700324 roleMap.unlock(deviceId);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700325 }
326 }
327
Ayaka Koshibec4047702014-10-07 14:43:52 -0700328 @Override
Madan Jampanif7536ab2015-05-07 23:23:23 -0700329 public CompletableFuture<MastershipEvent> relinquishRole(NodeId nodeId, DeviceId deviceId) {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700330 // relinquishRole is basically set to None
331
332 // If nodeId was master reelect next and remove nodeId
333 // else remove from STANDBY
Ayaka Koshibe25fd23a2014-10-03 15:50:43 -0700334
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700335 roleMap.lock(deviceId);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700336 try {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700337 final RoleValue rv = getRoleValue(deviceId);
338 final MastershipRole currentRole = rv.getRole(nodeId);
339 switch (currentRole) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700340 case MASTER:
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700341 NodeId newMaster = reelect(nodeId, deviceId, rv);
342 if (newMaster != null) {
Yuta HIGUCHIf1d159a2014-10-29 23:31:40 -0700343 updateTerm(deviceId);
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700344 roleMap.put(deviceId, rv);
Madan Jampanif7536ab2015-05-07 23:23:23 -0700345 return CompletableFuture.completedFuture(
346 new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()));
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700347 } else {
Ayaka Koshibe98bd12f2014-11-01 20:13:37 -0700348 // No master candidate - no more backups, device is likely
349 // fully disconnected
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700350 roleMap.put(deviceId, rv);
351 // Should there be new event type?
Madan Jampanif7536ab2015-05-07 23:23:23 -0700352 return CompletableFuture.completedFuture(null);
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700353 }
Ayaka Koshibec4047702014-10-07 14:43:52 -0700354 case STANDBY:
355 //fall through to reinforce relinquishment
356 case NONE:
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700357 boolean modified = rv.reassign(nodeId, STANDBY, NONE);
358 if (modified) {
359 roleMap.put(deviceId, rv);
Madan Jampanif7536ab2015-05-07 23:23:23 -0700360 return CompletableFuture.completedFuture(
361 new MastershipEvent(BACKUPS_CHANGED, deviceId, rv.roleInfo()));
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700362 }
Madan Jampanif7536ab2015-05-07 23:23:23 -0700363 return CompletableFuture.completedFuture(null);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700364 default:
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700365 log.warn("unknown Mastership Role {}", currentRole);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700366 }
Madan Jampanif7536ab2015-05-07 23:23:23 -0700367 return CompletableFuture.completedFuture(null);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700368 } finally {
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700369 roleMap.unlock(deviceId);
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700370 }
Ayaka Koshibed9f693e2014-09-29 18:04:54 -0700371 }
372
HIGUCHI Yuta59f02292015-02-25 19:51:48 -0800373 @Override
374 public void relinquishAllRole(NodeId nodeId) {
375
376 List<MastershipEvent> events = new ArrayList<>();
377 for (Entry<DeviceId, RoleValue> entry : roleMap.entrySet()) {
378 final DeviceId deviceId = entry.getKey();
379 final RoleValue roleValue = entry.getValue();
380
381 if (roleValue.contains(MASTER, nodeId) ||
382 roleValue.contains(STANDBY, nodeId)) {
383
Madan Jampanif7536ab2015-05-07 23:23:23 -0700384 relinquishRole(nodeId, deviceId).whenComplete((event, error) -> {
385 if (event != null) {
386 events.add(event);
387 }
388 });
HIGUCHI Yuta59f02292015-02-25 19:51:48 -0800389 }
390 }
391 notifyDelegate(events);
392 }
393
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700394 // TODO: Consider moving this to RoleValue method
Ayaka Koshibec4047702014-10-07 14:43:52 -0700395 //helper to fetch a new master candidate for a given device.
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700396 private NodeId reelect(
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700397 NodeId current, DeviceId deviceId, RoleValue rv) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700398
399 //if this is an queue it'd be neater.
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700400 NodeId candidate = null;
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700401 for (NodeId n : rv.nodesOfRole(STANDBY)) {
402 if (!current.equals(n)) {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700403 candidate = n;
Ayaka Koshibec4047702014-10-07 14:43:52 -0700404 break;
405 }
406 }
407
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700408 if (candidate == null) {
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700409 log.info("{} giving up and going to NONE for {}", current, deviceId);
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700410 rv.remove(MASTER, current);
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700411 // master did change, but there is no master candidate.
Ayaka Koshibec4047702014-10-07 14:43:52 -0700412 return null;
413 } else {
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700414 log.info("{} trying to pass mastership for {} to {}", current, deviceId, candidate);
415 rv.replace(current, candidate, MASTER);
416 rv.reassign(candidate, STANDBY, NONE);
417 return candidate;
Ayaka Koshibec4047702014-10-07 14:43:52 -0700418 }
419 }
420
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700421 //return the RoleValue structure for a device, or create one
422 private RoleValue getRoleValue(DeviceId deviceId) {
423 RoleValue value = roleMap.get(deviceId);
424 if (value == null) {
425 value = new RoleValue();
Ayaka Koshibea7384a82014-10-22 18:59:06 -0700426 RoleValue concurrentlyAdded = roleMap.putIfAbsent(deviceId, value);
427 if (concurrentlyAdded != null) {
428 return concurrentlyAdded;
429 }
Ayaka Koshibec4047702014-10-07 14:43:52 -0700430 }
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700431 return value;
Ayaka Koshibec4047702014-10-07 14:43:52 -0700432 }
433
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700434 //get first applicable node out of store-unique structure.
435 private NodeId getNode(MastershipRole role, DeviceId deviceId) {
436 RoleValue value = roleMap.get(deviceId);
437 if (value != null) {
438 return value.get(role);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700439 }
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700440 return null;
441 }
442
Ayaka Koshibec4047702014-10-07 14:43:52 -0700443 //adds or updates term information.
Yuta HIGUCHIb5b71b32014-10-30 02:41:25 -0700444 // must be guarded by roleMap.lock(deviceId)
Ayaka Koshibef9b02fc2014-10-15 17:07:05 -0700445 private void updateTerm(DeviceId deviceId) {
Yuta HIGUCHIf1d159a2014-10-29 23:31:40 -0700446 Integer term = terms.get(deviceId);
447 if (term == null) {
448 term = terms.putIfAbsent(deviceId, INIT);
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700449 if (term == null) {
Yuta HIGUCHIf1d159a2014-10-29 23:31:40 -0700450 // initial term set successfully
451 return;
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700452 }
Yuta HIGUCHIf1d159a2014-10-29 23:31:40 -0700453 // concurrent initialization detected,
454 // fall through to try incrementing
455 }
456 Integer nextTerm = term + 1;
457 boolean success = terms.replace(deviceId, term, nextTerm);
458 while (!success) {
459 term = terms.get(deviceId);
460 if (term == null) {
461 // something is very wrong, but write something to avoid
462 // infinite loop.
463 log.warn("Term info for {} disappeared.", deviceId);
464 term = putIfAbsent(terms, deviceId, nextTerm);
465 }
466 nextTerm = term + 1;
467 success = terms.replace(deviceId, term, nextTerm);
Ayaka Koshibec4047702014-10-07 14:43:52 -0700468 }
Ayaka Koshibe8583ff32014-10-02 16:25:30 -0700469 }
470
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700471 private class RemoteMasterShipEventHandler implements EntryListener<DeviceId, RoleValue> {
alshabib339a3d92014-09-26 17:54:32 -0700472
473 @Override
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700474 public void entryAdded(EntryEvent<DeviceId, RoleValue> event) {
Yuta HIGUCHIeb5a0b92014-10-29 15:45:55 -0700475 entryUpdated(event);
alshabib339a3d92014-09-26 17:54:32 -0700476 }
477
478 @Override
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700479 public void entryRemoved(EntryEvent<DeviceId, RoleValue> event) {
alshabib339a3d92014-09-26 17:54:32 -0700480 }
481
482 @Override
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700483 public void entryUpdated(EntryEvent<DeviceId, RoleValue> event) {
Ayaka Koshibe98bd12f2014-11-01 20:13:37 -0700484 // compare old and current RoleValues. If master is different,
485 // emit MASTER_CHANGED. else, emit BACKUPS_CHANGED.
486 RoleValue oldValue = event.getOldValue();
487 RoleValue newValue = event.getValue();
488
Ayaka Koshibe15f4d602014-11-03 16:26:27 -0800489 // There will be no oldValue at the very first instance of an EntryEvent.
490 // Technically, the progression is: null event -> null master -> some master;
491 // We say a null master and a null oldValue are the same condition.
Yuta HIGUCHI780b1382014-11-03 14:36:39 -0800492 NodeId oldMaster = null;
493 if (oldValue != null) {
494 oldMaster = oldValue.get(MASTER);
495 }
496 NodeId newMaster = newValue.get(MASTER);
497
Ayaka Koshibe15f4d602014-11-03 16:26:27 -0800498 if (!Objects.equal(oldMaster, newMaster)) {
Ayaka Koshibe98bd12f2014-11-01 20:13:37 -0700499 notifyDelegate(new MastershipEvent(
500 MASTER_CHANGED, event.getKey(), event.getValue().roleInfo()));
501 } else {
502 notifyDelegate(new MastershipEvent(
503 BACKUPS_CHANGED, event.getKey(), event.getValue().roleInfo()));
504 }
Ayaka Koshibeb5c63a02014-10-18 18:42:27 -0700505 }
506
507 @Override
508 public void entryEvicted(EntryEvent<DeviceId, RoleValue> event) {
509 }
510
511 @Override
512 public void mapEvicted(MapEvent event) {
513 }
514
515 @Override
516 public void mapCleared(MapEvent event) {
alshabib339a3d92014-09-26 17:54:32 -0700517 }
518 }
519
tomb41d1ac2014-09-24 01:51:24 -0700520}