/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.spi.loadbalancing.roundrobin;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.ignite.cluster.ClusterNode;
/**
* Load balancer for per-task configuration.
*/
class RoundRobinPerTaskLoadBalancer {
/** Balancing nodes. */
private ArrayDeque<ClusterNode> nodeQueue;
/** Jobs mapped flag. */
private volatile boolean isMapped;
/** Mutex. */
private final Object mux = new Object();
/**
* Call back for job mapped event.
*/
void onMapped() {
isMapped = true;
}
/**
* Gets balanced node for given topology. This implementation
* is to be used only from {@link org.apache.ignite.compute.ComputeTask#map(List, Object)} method
* and, therefore, does not need to be thread-safe.
*
* @param top Topology to pick from.
* @return Best balanced node.
*/
ClusterNode getBalancedNode(List<ClusterNode> top) {
assert top != null;
assert !top.isEmpty();
boolean readjust = isMapped;
synchronized (mux) {
// Populate first time.
if (nodeQueue == null)
nodeQueue = new ArrayDeque<>(top);
// If job has been mapped, then it means
// that it is most likely being failed over.
// In this case topology might have changed
// and we need to readjust with every apply.
if (readjust)
// Add missing nodes.
for (ClusterNode node : top)
if (!nodeQueue.contains(node))
nodeQueue.offer(node);
ClusterNode next = nodeQueue.poll();
// If jobs have been mapped, we need to make sure
// that queued node is still in topology.
if (readjust && next != null) {
while (!top.contains(next) && !nodeQueue.isEmpty())
next = nodeQueue.poll();
// No nodes found and queue is empty.
if (next != null && !top.contains(next))
return null;
}
if (next != null)
// Add to the end.
nodeQueue.offer(next);
return next;
}
}
/**
* THIS METHOD IS USED ONLY FOR TESTING.
*
* @return Internal list of nodes.
*/
List<ClusterNode> getNodes() {
synchronized (mux) {
return Collections.unmodifiableList(new ArrayList<>(nodeQueue));
}
}
}