/* dCache - http://www.dcache.org/
*
* Copyright (C) 2015 Deutsches Elektronen-Synchrotron
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.srm.scheduler.strategy;
import com.google.common.collect.ConcurrentHashMultiset;
import com.google.common.collect.Multiset;
import com.google.common.collect.Ordering;
import java.util.ArrayDeque;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import org.dcache.srm.request.Job;
import org.dcache.srm.scheduler.Scheduler;
import org.dcache.srm.scheduler.State;
import org.dcache.srm.scheduler.StateChangeListener;
import org.dcache.srm.scheduler.spi.SchedulingStrategy;
import static org.dcache.srm.scheduler.State.*;
public class InProgressFairShareSchedulingStrategy extends DiscriminatingSchedulingStrategy implements SchedulingStrategy, StateChangeListener
{
private static final EnumSet<State> RUNNING_STATES =
EnumSet.of(INPROGRESS, RQUEUED, READY, TRANSFERRING);
private final Map<String,Queue<Long>> jobs = new HashMap<>();
private final Multiset<String> counters = ConcurrentHashMultiset.create();
private int size;
private final Ordering<Map.Entry<String, Queue<Long>>> byCount =
Ordering.natural().onResultOf(entry -> counters.count(entry.getKey()));
public InProgressFairShareSchedulingStrategy(Scheduler scheduler, String discriminator)
{
super(discriminator);
scheduler.addStateChangeListener(this);
}
@Override
public void stateChanged(Job job, State oldState, State newState)
{
if (RUNNING_STATES.contains(oldState) && !RUNNING_STATES.contains(newState)) {
counters.remove(getDiscriminatingValue(job));
} else if (!RUNNING_STATES.contains(oldState) && RUNNING_STATES.contains(newState)) {
counters.add(getDiscriminatingValue(job));
}
}
@Override
public synchronized Long remove()
{
if (size == 0) {
return null;
}
Map.Entry<String,Queue<Long>> entry = byCount.min(jobs.entrySet());
Queue<Long> queue = entry.getValue();
Long job = queue.remove();
if (queue.isEmpty()) {
jobs.remove(entry.getKey());
}
size--;
return job;
}
@Override
public synchronized int size()
{
return size;
}
@Override
protected synchronized void add(String key, Job job)
{
jobs.computeIfAbsent(key, k -> new ArrayDeque<>()).add(job.getId());
size++;
}
}