Skip to content

Commit b97d07e

Browse files
committed
[task-controller] parallel reconciliation settable from cli
1 parent 17ad2fb commit b97d07e

3 files changed

Lines changed: 29 additions & 20 deletions

File tree

control-operator/cmd/task-manager/main.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,14 @@ func main() {
5757
var metricsAddr string
5858
var enableLeaderElection bool
5959
var probeAddr string
60+
var maxConcurrentReconciles int
6061
flag.StringVar(&metricsAddr, "metrics-bind-address", ":9082", "The address the metric endpoint binds to.")
6162
flag.StringVar(&probeAddr, "health-probe-bind-address", ":9083", "The address the probe endpoint binds to.")
6263
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
6364
"Enable leader election for controller manager. "+
6465
"Enabling this will ensure there is only one active controller manager.")
66+
flag.IntVar(&maxConcurrentReconciles, "max-concurrent-reconciles", 1,
67+
"Number of Tasks that are reconciled in parallel. Reconciles of a single Task are always serialized.")
6568
opts := zap.Options{
6669
Development: true,
6770
}
@@ -90,10 +93,11 @@ func main() {
9093
}
9194

9295
if err = (&controller.TaskReconciler{
93-
Client: mgr.GetClient(),
94-
Scheme: mgr.GetScheme(),
95-
Recorder: mgr.GetEventRecorderFor("task-controller"),
96-
NodeName: nodeName,
96+
Client: mgr.GetClient(),
97+
Scheme: mgr.GetScheme(),
98+
Recorder: mgr.GetEventRecorderFor("task-controller"),
99+
NodeName: nodeName,
100+
MaxConcurrentReconciles: maxConcurrentReconciles,
97101
}).SetupWithManager(mgr); err != nil {
98102
setupLog.Error(err, "unable to create controller", "controller", "Task")
99103
os.Exit(1)

control-operator/config/manager/task-manager/task-manager.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ spec:
3333
args:
3434
- --health-probe-bind-address=:9082
3535
- --metrics-bind-address=:9083
36+
- --max-concurrent-reconciles=5
3637
- --zap-encoder=json
3738
env:
3839
- name: NODE_NAME

control-operator/internal/controller/task_controller.go

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"fmt"
3030
"reflect"
3131
"strings"
32+
"sync"
3233
"time"
3334

3435
v1 "k8s.io/api/core/v1"
@@ -53,12 +54,13 @@ import (
5354
// TaskReconciler reconciles a Task object
5455
type TaskReconciler struct {
5556
client.Client
56-
Scheme *runtime.Scheme
57-
Recorder record.EventRecorder
58-
NodeName string
57+
Scheme *runtime.Scheme
58+
Recorder record.EventRecorder
59+
NodeName string
60+
MaxConcurrentReconciles int
5961
}
6062

61-
var clientsForContainers map[string]*OccClient = make(map[string]*OccClient)
63+
var clientsForContainers sync.Map
6264

6365
const taskFinalizer string = "aliecs.alice.cern/finalizer"
6466

@@ -135,7 +137,7 @@ func (r *TaskReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
135137
return ctrl.Result{}, nil
136138
}
137139

138-
if _, exists := clientsForContainers[t.Name]; !exists {
140+
if _, exists := clientsForContainers.Load(t.Name); !exists {
139141
if existingPod.Status.PodIP == "" {
140142
log.Info("pod doesn't have IP yet, we wait for different event")
141143
return ctrl.Result{}, nil
@@ -158,12 +160,12 @@ func (r *TaskReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
158160
// on them being implemented
159161
if t.Status.State == "" {
160162
log.V(1).Info("Status.State is empty, querying container")
161-
client, exists := clientsForContainers[t.Name]
163+
client, exists := clientsForContainers.Load(t.Name)
162164
if !exists {
163165
return ctrl.Result{Requeue: true}, nil
164166
}
165167

166-
stateReply, err := client.GetState(ctx)
168+
stateReply, err := client.(*OccClient).GetState(ctx)
167169
if err != nil {
168170
log.Error(err, "Failed to GetState")
169171
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
@@ -187,16 +189,18 @@ func (r *TaskReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
187189

188190
// Handle Spec -> gRPC State Sync
189191
if t.Status.State != t.Spec.State {
190-
client, exists := clientsForContainers[t.Name]
192+
clientAny, exists := clientsForContainers.Load(t.Name)
191193
if !exists {
192194
return ctrl.Result{Requeue: true}, nil
193195
}
194196

197+
client := clientAny.(*OccClient)
198+
195199
stateReply, err := client.GetState(ctx)
196200
if err != nil {
197201
log.Info("Failed to get state for sync, retrying in 5s", "error", err.Error())
198202
client.Close()
199-
delete(clientsForContainers, t.Name)
203+
clientsForContainers.Delete(t.Name)
200204
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
201205
}
202206

@@ -267,7 +271,7 @@ func (r *TaskReconciler) createGRPCConsumer(ctx context.Context, t *aliecsv1alph
267271
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
268272
}
269273

270-
clientsForContainers[t.Name] = client
274+
clientsForContainers.Store(t.Name, client)
271275

272276
if err := r.recordCondition(ctx, t, aliecsv1alpha1.ConditionGRPCConnected, metav1.ConditionTrue, "Connected", fmt.Sprintf("gRPC connection established to %s", addr)); err != nil {
273277
return ctrl.Result{}, err
@@ -276,14 +280,14 @@ func (r *TaskReconciler) createGRPCConsumer(ctx context.Context, t *aliecsv1alph
276280
}
277281

278282
func (r *TaskReconciler) consumeGRPCConsumerIfReady(ctx context.Context, t *aliecsv1alpha1.Task, log logr.Logger) ctrl.Result {
279-
client, exists := clientsForContainers[t.Name]
283+
client, exists := clientsForContainers.Load(t.Name)
280284

281285
if !exists {
282286
log.Info("didn't found existing client, retrying ", "task", t.Name)
283287
return ctrl.Result{RequeueAfter: time.Second}
284288
}
285289

286-
if !client.ConsumeIfReady(ctx) {
290+
if !client.(*OccClient).ConsumeIfReady(ctx) {
287291
log.Info("gRPC client is not ready, retrying in 5 seconds", "name", t.Name)
288292
return ctrl.Result{RequeueAfter: 5 * time.Second}
289293
}
@@ -341,12 +345,12 @@ func (r *TaskReconciler) deletePod(ctx context.Context, t *aliecsv1alpha1.Task,
341345
}
342346

343347
func (*TaskReconciler) cleargRPC(t *aliecsv1alpha1.Task, log logr.Logger) {
344-
if client, exists := clientsForContainers[t.Name]; exists {
348+
if client, exists := clientsForContainers.Load(t.Name); exists {
345349
log.Info("Cleaning up gRPC connection")
346-
if err := client.Close(); err != nil {
350+
if err := client.(*OccClient).Close(); err != nil {
347351
log.Error(err, "Failed to close gRPC client during deletion")
348352
}
349-
delete(clientsForContainers, t.Name)
353+
clientsForContainers.Delete(t.Name)
350354
log.Info("gRPC cleaned")
351355
}
352356
}
@@ -431,7 +435,7 @@ func (r *TaskReconciler) SetupWithManager(mgr ctrl.Manager) error {
431435
}),
432436
)).
433437
Owns(&v1.Pod{}).
434-
WithOptions(controller.Options{MaxConcurrentReconciles: 1}).
438+
WithOptions(controller.Options{MaxConcurrentReconciles: r.MaxConcurrentReconciles}).
435439
Complete(r)
436440
}
437441

0 commit comments

Comments
 (0)