private void onSelectOwner(SelectOwner selectOwner) {
String currentOwner = getCurrentOwner(selectOwner.getEntityPath());
if(Strings.isNullOrEmpty(currentOwner)) {
+ String entityType = EntityOwnersModel.entityTypeFromEntityPath(selectOwner.getEntityPath());
writeNewOwner(selectOwner.getEntityPath(), newOwner(selectOwner.getAllCandidates(),
+ entityOwnershipStatistics.byEntityType(entityType),
selectOwner.getOwnerSelectionStrategy()));
Cancellable cancellable = entityToScheduledOwnershipTask.get(selectOwner.getEntityPath());
if(isLeader()) {
String currentOwner = getCurrentOwner(message.getEntityPath());
if(message.getRemovedCandidate().equals(currentOwner)){
+ String entityType = EntityOwnersModel.entityTypeFromEntityPath(message.getEntityPath());
writeNewOwner(message.getEntityPath(),
- newOwner(message.getRemainingCandidates(), getEntityOwnerElectionStrategy(message.getEntityPath())));
+ newOwner(message.getRemainingCandidates(), entityOwnershipStatistics.byEntityType(entityType),
+ getEntityOwnerElectionStrategy(message.getEntityPath())));
}
} else {
// We're not the leader. If the removed candidate is our local member then check if we actually
EntityOwnerSelectionStrategy strategy = getEntityOwnerElectionStrategy(message.getEntityPath());
if(Strings.isNullOrEmpty(currentOwner)){
if(strategy.getSelectionDelayInMillis() == 0L) {
- writeNewOwner(message.getEntityPath(), newOwner(message.getAllCandidates(), strategy));
+ String entityType = EntityOwnersModel.entityTypeFromEntityPath(message.getEntityPath());
+ writeNewOwner(message.getEntityPath(), newOwner(message.getAllCandidates(),
+ entityOwnershipStatistics.byEntityType(entityType), strategy));
} else {
scheduleOwnerSelection(message.getEntityPath(), message.getAllCandidates(), strategy);
}
node(entityTypeNode.getIdentifier()).node(ENTITY_NODE_ID).node(entityNode.getIdentifier()).
node(ENTITY_OWNER_NODE_ID).build();
- Object newOwner = newOwner(getCandidateNames(entityNode), getEntityOwnerElectionStrategy(entityPath));
+ String entityType = EntityOwnersModel.entityTypeFromEntityPath(entityPath);
+
+ Object newOwner = newOwner(getCandidateNames(entityNode),
+ entityOwnershipStatistics.byEntityType(entityType),
+ getEntityOwnerElectionStrategy(entityPath));
LOG.debug("{}: Found entity {}, writing new owner {}", persistenceId(), entityPath, newOwner);
entityToScheduledOwnershipTask.put(entityPath, lastScheduledTask);
}
- private String newOwner(Collection<String> candidates, EntityOwnerSelectionStrategy ownerSelectionStrategy) {
+ private String newOwner(Collection<String> candidates, Map<String, Long> statistics, EntityOwnerSelectionStrategy ownerSelectionStrategy) {
Collection<String> viableCandidates = getViableCandidates(candidates);
if(viableCandidates.size() == 0){
return "";
}
- return ownerSelectionStrategy.newOwner(viableCandidates);
+ return ownerSelectionStrategy.newOwner(viableCandidates, statistics);
}
private Collection<String> getViableCandidates(Collection<String> candidates) {
}
Map<String, Long> byEntityType(String entityType){
- return statistics.get(entityType).readOnlySnapshot();
+ if(statistics.get(entityType) != null) {
+ return statistics.get(entityType).readOnlySnapshot();
+ }
+ return new HashMap<>();
}
private void updateStatistics(String entityType, String candidateName, long count){
package org.opendaylight.controller.cluster.datastore.entityownership.selectionstrategy;
import java.util.Collection;
+import java.util.Map;
/**
* An EntityOwnerSelectionStrategy is to be used by the EntityOwnershipShard to select a new owner from a collection
/**
*
* @param viableCandidates the available candidates from which to choose the new owner
+ * @param statistics contains a snapshot of a mapping between candidate names and the number of entities
+ * owned by that candidate
* @return the new owner
*/
- String newOwner(Collection<String> viableCandidates);
+ String newOwner(Collection<String> viableCandidates, Map<String, Long> statistics);
}
import com.google.common.base.Preconditions;
import java.util.Collection;
+import java.util.Map;
/**
* The FirstCandidateSelectionStrategy always selects the first viable candidate from the list of candidates
}
@Override
- public String newOwner(Collection<String> viableCandidates) {
+ public String newOwner(Collection<String> viableCandidates, Map<String, Long> statistics) {
Preconditions.checkArgument(viableCandidates.size() > 0, "No viable candidates provided");
return viableCandidates.iterator().next();
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.entityownership.selectionstrategy;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * The LeastLoadedCandidateSelectionStrategy assigns ownership for an entity to the candidate which owns the least
+ * number of entities.
+ */
+public class LeastLoadedCandidateSelectionStrategy extends AbstractEntityOwnerSelectionStrategy {
+ protected LeastLoadedCandidateSelectionStrategy(long selectionDelayInMillis) {
+ super(selectionDelayInMillis);
+ }
+
+ @Override
+ public String newOwner(Collection<String> viableCandidates, Map<String, Long> statistics) {
+ String leastLoadedCandidate = null;
+ long leastLoadedCount = Long.MAX_VALUE;
+
+ for(String candidateName : viableCandidates){
+ Long val = statistics.get(candidateName);
+ if(val != null && val < leastLoadedCount){
+ leastLoadedCount = val;
+ leastLoadedCandidate = candidateName;
+ }
+ }
+
+ if(leastLoadedCandidate == null){
+ return viableCandidates.iterator().next();
+ }
+ return leastLoadedCandidate;
+ }
+}
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
public class LastCandidateSelectionStrategy extends AbstractEntityOwnerSelectionStrategy {
public LastCandidateSelectionStrategy(long selectionDelayInMillis) {
}
@Override
- public String newOwner(Collection<String> viableCandidates) {
+ public String newOwner(Collection<String> viableCandidates, Map<String, Long> statistics) {
List<String> candidates = new ArrayList<>(viableCandidates);
return candidates.get(candidates.size()-1);
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.datastore.entityownership.selectionstrategy;
+
+import static org.junit.Assert.assertEquals;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+
+public class LeastLoadedCandidateSelectionStrategyTest {
+
+ @Test
+ public void testLeastLoadedStrategy(){
+ LeastLoadedCandidateSelectionStrategy strategy = new LeastLoadedCandidateSelectionStrategy(0L);
+
+ String owner = strategy.newOwner(prepareViableCandidates(3), new HashMap<String, Long>());
+ assertEquals("member-1", owner);
+
+ // member-2 has least load
+ owner = strategy.newOwner(prepareViableCandidates(3), prepareStatistics(5,2,4));
+ assertEquals("member-2", owner);
+
+ // member-3 has least load
+ owner = strategy.newOwner(prepareViableCandidates(3), prepareStatistics(5,7,4));
+ assertEquals("member-3", owner);
+
+ // member-1 has least load
+ owner = strategy.newOwner(prepareViableCandidates(3), prepareStatistics(1,7,4));
+ assertEquals("member-1", owner);
+
+ }
+
+ private Map<String, Long> prepareStatistics(long... count){
+ Map<String, Long> statistics = new HashMap<>();
+ for(int i=0;i<count.length;i++){
+ statistics.put("member-" + (i+1), count[i]);
+ }
+ return statistics;
+ }
+
+ private Collection<String> prepareViableCandidates(int count){
+ Collection<String> viableCandidates = new ArrayList<>();
+ for(int i=0;i<count;i++){
+ viableCandidates.add("member-" + (i+1));
+ }
+ return viableCandidates;
+ }
+}
\ No newline at end of file