Skip to content

Commit 7b03978

Browse files
committed
remove unnecessary strategies and update docs
1 parent 0eb3064 commit 7b03978

File tree

3 files changed

+27
-37
lines changed

3 files changed

+27
-37
lines changed

docs/controller/scheduler.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ If not disabled, the scheduler requires a config that looks like this:
1010
```yaml
1111
scheduler:
1212
scope: Namespaced # optional
13-
strategy: Balanced # optional
13+
strategy: BalancedIgnoreEmpty # optional
1414

1515
selectors: # optional
1616
clusters: # optional
@@ -63,12 +63,13 @@ The following fields can be specified inside the `scheduler` field:
6363
- Determines whether the scheduler takes `Cluster` resources in all namespaces into accounts or only in a specific one.
6464
- In `Namespaced` mode, only `Cluster` resources from a single namespace are taken into account when checking for existing clusters to schedule requests to. If the cluster template that corresponds to the purpose specified in the request has a `metadata.namespace` set, this namespace is used to check for `Cluster` resources and also to create new ones. If not, the namespace of the `ClusterRequest` resource is used instead.
6565
- In `Cluster` mode, the scheduler takes all clusters into account when trying to find existing clusters that can be reused. New clusters are still created in the namespace specified in the cluster template, or in the request's namespace, if the former one is not set.
66-
- `strategy` _(optional, defaults to `Balanced`)_
67-
- Valid values: `Balanced`, `Random`, `Simple`
66+
- `strategy` _(optional, defaults to `BalancedIgnoreEmpty`)_
67+
- Valid values: `Balanced`, `BalancedIgnoreEmpty`, `Random`, `Simple`
6868
- Determines how the scheduler chooses a cluster if multiple existing ones qualify for a request.
6969
- With the `Balanced` strategy, the scheduler chooses the cluster with the fewest requests pointing to it. In case of a tie, the first one is chosen.
70-
- With the `Random` strategy, a cluster is chosen randomly.
71-
- With the `Simple` strategy, the first cluster in the list (should be in alphabetical order) is chosen.
70+
- For preemptive requests, it works the other way around and the fullest cluster is chosen.
71+
- `BalancedIgnoreEmpty` works like `Balanced`, but it favors clusters which already have regular requests on them over those that don't.
72+
- This is useful in combination with preemptive requests. The regular `Balanced` strategy will immediately schedule the next regular request on a new preemptively scheduled cluster, preventing the scheduler from deleting this cluster again until that regular request is removed. With `BalanceIgnoreEmpty`, the regular request would be scheduled to a different cluster if there was capacity left, which enables the scheduler to reschedule the preemptive requests on the otherwise empty cluster and remove it, if any regular requests are removed.
7273
- `selectors.clusters` _(optional)_
7374
- A label selector that restricts which `Cluster` resources are evaluated by the scheduler. Clusters that don't match the selector are treated as if they didn't exist.
7475
- The selector syntax is the default k8s one, as it is used in `Deployment` resources, for example.
@@ -125,6 +126,24 @@ Note that the `ClusterRequest` resource is removed immediately and does not wait
125126

126127
## Preemptive Scheduling
127128

128-
To avoid long waiting times for `ClusterRequest`s, it is possible to request clusters preemptively. A `ClusterRequest` with `spec.preemptive` set to `true` is referred to as a 'preemptive request'. These requests behave like regular requests, with one important difference: a regular request prefers taking over an existing `Cluster` belonging to a preemptive request over creating a new `Cluster`. If that happens, the replaced preemptive request will be rescheduled, potentially resulting in a new `Cluster`.
129+
To avoid long waiting times for `ClusterRequest`s, it is possible to request clusters preemptively. 'Preemptive requests' behave like regular requests, with one important difference: a regular request prefers taking over an existing `Cluster` belonging to a preemptive request over creating a new `Cluster`. If that happens, the replaced preemptive request will be rescheduled, potentially resulting in a new `Cluster`.
129130

130131
Think of preemptive requests as reservations for clusters (or workload capacity on shared clusters) which are used by regular requests.
132+
133+
The resource for preemptive requests looks like this:
134+
```yaml
135+
apiVersion: clusters.openmcp.cloud/v1alpha1
136+
kind: PreemptiveClusterRequest
137+
metadata:
138+
name: my-request
139+
namespace: my-namespace
140+
spec:
141+
purpose: workload
142+
workload: 3
143+
```
144+
145+
The `spec.workload` field specifies for how many regular requests this preemptive request should account. This is a convenience feature - if you want to reserve capacity for 20 regular requests, you only have to create a single preemptive request, not 20.
146+
147+
A relevant difference between regular and preemptive requests is that the latter ones are allowed to be 'rescheduled' to other clusters. The scheduler will occasionally do this even if no preemptive request was replaced by a regular one to optimize cluster usage. The effectiveness of this also depends on the chosen strategy.
148+
149+
Note that the behavior of preemptive requests depends on the tenancy of the request's purpose, as defined in the scheduler configuration. This means that preemptive requests on clusters with multiple purposes might behave differently.

internal/config/config_scheduler.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ type Strategy string
4444
const (
4545
STRATEGY_BALANCED_IGNORE_EMPTY Strategy = "BalancedIgnoreEmpty"
4646
STRATEGY_BALANCED Strategy = "Balanced"
47-
STRATEGY_RANDOM Strategy = "Random"
48-
STRATEGY_SIMPLE Strategy = "Simple"
4947
)
5048

5149
type ClusterDefinition struct {
@@ -89,7 +87,7 @@ func (c *SchedulerConfig) Validate(fldPath *field.Path) error {
8987
if !slices.Contains(validScopes, string(c.Scope)) {
9088
errs = append(errs, field.NotSupported(fldPath.Child("scope"), string(c.Scope), validScopes))
9189
}
92-
validStrategies := []string{string(STRATEGY_BALANCED), string(STRATEGY_RANDOM), string(STRATEGY_SIMPLE)}
90+
validStrategies := []string{string(STRATEGY_BALANCED), string(STRATEGY_BALANCED_IGNORE_EMPTY)}
9391
if !slices.Contains(validStrategies, string(c.Strategy)) {
9492
errs = append(errs, field.NotSupported(fldPath.Child("strategy"), string(c.Strategy), validStrategies))
9593
}

internal/controllers/scheduler/strategy/strategy.go

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package strategy
22

33
import (
44
"context"
5-
"math/rand/v2"
65

76
clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1"
87
"github.com/openmcp-project/openmcp-operator/internal/config"
@@ -42,41 +41,15 @@ func Implement[T any](name string, chooseFunc func(ctx context.Context, clusterD
4241
// If the value is not recognized, it defaults to Balanced.
4342
func FromConfig[T any](s config.Strategy) Strategy[T] {
4443
switch s {
45-
case config.STRATEGY_RANDOM:
46-
return Random[T]()
47-
case config.STRATEGY_SIMPLE:
48-
return Simple[T]()
4944
case config.STRATEGY_BALANCED:
5045
return Balanced[T]()
5146
case config.STRATEGY_BALANCED_IGNORE_EMPTY:
5247
return BalancedIgnoreEmpty[T]()
5348
default:
54-
return Balanced[T]()
49+
return BalancedIgnoreEmpty[T]()
5550
}
5651
}
5752

58-
// Random chooses a cluster at random.
59-
func Random[T any]() Strategy[T] {
60-
return Implement("Random", func(ctx context.Context, clusterData []T, getCluster func(T) *clustersv1alpha1.Cluster, cDef *config.ClusterDefinition, preemptive bool) (T, error) {
61-
var zero T
62-
if len(clusterData) == 0 {
63-
return zero, nil
64-
}
65-
return clusterData[rand.IntN(len(clusterData))], nil
66-
})
67-
}
68-
69-
// Simple chooses the first cluster in the list.
70-
func Simple[T any]() Strategy[T] {
71-
return Implement("Simple", func(ctx context.Context, clusterData []T, getCluster func(T) *clustersv1alpha1.Cluster, cDef *config.ClusterDefinition, preemptive bool) (T, error) {
72-
var zero T
73-
if len(clusterData) == 0 {
74-
return zero, nil
75-
}
76-
return clusterData[0], nil
77-
})
78-
}
79-
8053
// Balanced chooses the cluster with the least number of requests, or the first one in case of a tie (preemptive requests are ignored).
8154
// For preemptive requests, the logic works the other way around and tries to find the cluster with the most requests (including preemptive ones).
8255
func Balanced[T any]() Strategy[T] {

0 commit comments

Comments
 (0)