ref: simplify scheduler constructor

just take the list of nodes directly.
This commit is contained in:
2026-06-10 00:25:47 +02:00
parent f663095134
commit 64bb8c342a
4 changed files with 44 additions and 28 deletions

View File

@@ -16,6 +16,7 @@
package nextflow.k8s package nextflow.k8s
import groovy.transform.CompileDynamic
import nextflow.k8s.strategies.K8sHashSchedulingStrategy import nextflow.k8s.strategies.K8sHashSchedulingStrategy
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -97,11 +98,21 @@ class K8sExecutor extends Executor implements ExtensionPoint {
this.runtimeRecorder = new K8sRuntimeRecorder(k8sConfig.recordTaskRuntimes, k8sConfig.runtimeRecordPath) this.runtimeRecorder = new K8sRuntimeRecorder(k8sConfig.recordTaskRuntimes, k8sConfig.runtimeRecordPath)
this.runtimeEstimator = new K8sRuntimeEstimator(k8sConfig.runtimeRecordPath) this.runtimeEstimator = new K8sRuntimeEstimator(k8sConfig.runtimeRecordPath)
this.taskScheduler = new K8sTaskScheduler(this, new K8sHashSchedulingStrategy()) this.taskScheduler = new K8sTaskScheduler(getNodeList(), new K8sHashSchedulingStrategy())
this.schedulerThread = new Thread(this.taskScheduler) this.schedulerThread = new Thread(this.taskScheduler)
this.schedulerThread.start() this.schedulerThread.start()
} }
@CompileDynamic
private String[] getNodeList() {
final resp = getClient().nodeList()
ArrayList<String> nodes = new ArrayList<String>()
for ( Map item : resp.items ) {
nodes.add(item.metadata.name as String)
}
return nodes.toArray()
}
@Override @Override
void shutdown() { void shutdown() {
this.runtimeRecorder.write() this.runtimeRecorder.write()

View File

@@ -7,8 +7,15 @@ interface K8sSchedulingStrategy {
* *
* @param scheduler the calling scheduler object * @param scheduler the calling scheduler object
* @param queue Pending scheduling requests * @param queue Pending scheduling requests
* @param nodes Available Kubernetes node names
* @return A launch decision, or {@code null} when no task should be launched now * @return A launch decision, or {@code null} when no task should be launched now
*/ */
K8sSchedulingDecision schedule(K8sTaskScheduler scheduler, List<K8sSchedulingRequest> queue, List<String> nodes) K8sSchedulingDecision schedule(K8sTaskScheduler scheduler, List<K8sSchedulingRequest> queue)
/**
* Decides if the scheduler should immediately invoke the @ref schedule method of the strategy
* @param scheduler the calling scheduler object
* @param queue pending scheduling requests
* @return {@code true} if @ref schedule should immediately be called
*/
boolean scheduleImmediately(K8sTaskScheduler scheduler, List<K8sSchedulingRequest> queue)
} }

View File

@@ -12,18 +12,19 @@ class K8sTaskScheduler implements Runnable {
private final K8sExecutor executor private final K8sExecutor executor
private final K8sSchedulingStrategy strategy private final K8sSchedulingStrategy strategy
private final LinkedBlockingQueue<K8sSchedulingRequest> queue = new LinkedBlockingQueue<>() private final LinkedBlockingQueue<K8sSchedulingRequest> queue = new LinkedBlockingQueue<>()
private List<String> cachedNodes private String[] nodes
private synchronized boolean shouldStop private synchronized boolean shouldStop
private HashMap<String, ArrayList<K8sTaskHandler>> occupancy; private HashMap<String, ArrayList<K8sTaskHandler>> occupancy;
private HashMap<String, String> taskToNode private HashMap<String, String> taskToNode
K8sTaskScheduler(K8sExecutor executor, K8sSchedulingStrategy strategy) { K8sTaskScheduler(String[] nodes, K8sSchedulingStrategy strategy) {
this.executor = executor this.executor = executor
this.strategy = strategy this.strategy = strategy
this.nodes = nodes
this.occupancy = new HashMap<>() this.occupancy = new HashMap<>()
for (String node : getNodes()) { for (String node : nodes) {
this.occupancy.put(node, new ArrayList<K8sTaskHandler>()) this.occupancy.put(node, new ArrayList<K8sTaskHandler>())
} }
this.taskToNode = new HashMap<>() this.taskToNode = new HashMap<>()
@@ -36,8 +37,9 @@ class K8sTaskScheduler implements Runnable {
void submit(K8sTaskHandler handler) { void submit(K8sTaskHandler handler) {
log.info "[K8s] received queued task ${handler.task.name}" log.info "[K8s] received queued task ${handler.task.name}"
queue.add(new K8sSchedulingRequest(handler)) queue.add(new K8sSchedulingRequest(handler))
final pending = new ArrayList<K8sSchedulingRequest>(queue)
/* TODO: Decide if we should invoke the scheduler immediately */ if (strategy.scheduleImmediately(this, pending))
drain()
} }
/** /**
@@ -63,7 +65,7 @@ class K8sTaskScheduler implements Runnable {
protected synchronized void drain() { protected synchronized void drain() {
while( true ) { while( true ) {
final pending = new ArrayList<K8sSchedulingRequest>(queue) final pending = new ArrayList<K8sSchedulingRequest>(queue)
final decision = strategy.schedule(this, pending, getNodes()) final decision = strategy.schedule(this, pending)
if ( !decision ) if ( !decision )
return return
@@ -86,6 +88,10 @@ class K8sTaskScheduler implements Runnable {
unoccupied.keySet().toList() unoccupied.keySet().toList()
} }
List<String> getNodes() {
return nodes.toList()
}
/** /**
* Run is the scheduler threads main function * Run is the scheduler threads main function
* */ * */
@@ -106,20 +112,4 @@ class K8sTaskScheduler implements Runnable {
log.info("[K8s] stopping scheduler loop") log.info("[K8s] stopping scheduler loop")
this.shouldStop = true this.shouldStop = true
} }
protected List<String> getNodes() {
if ( cachedNodes == null )
cachedNodes = fetchNodes()
return cachedNodes
}
@CompileDynamic
private List<String> fetchNodes() {
final resp = executor.getClient().nodeList()
ArrayList<String> nodes = new ArrayList<String>()
for ( Map item : resp.items ) {
nodes.add(item.metadata.name as String)
}
return nodes
}
} }

View File

@@ -10,8 +10,8 @@ import nextflow.k8s.K8sTaskScheduler
class K8sHashSchedulingStrategy implements K8sSchedulingStrategy { class K8sHashSchedulingStrategy implements K8sSchedulingStrategy {
@Override @Override
K8sSchedulingDecision schedule(K8sTaskScheduler scheduler, List<K8sSchedulingRequest> queue, List<String> nodes) { K8sSchedulingDecision schedule(K8sTaskScheduler scheduler, List<K8sSchedulingRequest> queue) {
if ( !queue || !nodes ) if (!queue)
return null return null
final freeNodes = scheduler.freeNodes final freeNodes = scheduler.freeNodes
@@ -22,4 +22,12 @@ class K8sHashSchedulingStrategy implements K8sSchedulingStrategy {
final index = Math.floorMod(request.task.hash.asInt(), freeNodes.size()) final index = Math.floorMod(request.task.hash.asInt(), freeNodes.size())
return new K8sSchedulingDecision(request, freeNodes[index]) return new K8sSchedulingDecision(request, freeNodes[index])
} }
@Override
boolean scheduleImmediately(K8sTaskScheduler scheduler, List<K8sSchedulingRequest> queue) {
if (!queue)
return false
final freeNodes = scheduler.freeNodes
return freeNodes.size() > 0
}
} }