Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small question. #1

Open
iwasaki-kenta opened this issue May 1, 2019 · 2 comments
Open

Small question. #1

iwasaki-kenta opened this issue May 1, 2019 · 2 comments

Comments

@iwasaki-kenta
Copy link

iwasaki-kenta commented May 1, 2019

err = c.Watch(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForOwner{

Just out of curiosity, wouldn't watching for all pod events (pending, running, terminating, etc.) cause a race condition in the reconciliation loop where pods might keep getting created (even when a sufficient amount of pods as requested in the spec were created)?

The way I am looking at it is if a pod gets spawned (which concurrently triggers the reconciliation loop again), then the number of running pods would not have updated in time and thus cause another pod to be erroneously spawned.

If not, what exactly is preventing such a race condition from happening?

@xcoulon
Copy link
Owner

xcoulon commented May 10, 2019

hello @iwasaki-kenta and sorry for the late response. I don't think there's a risk of race condition here: the controller receives one event at a time, and processes it accordingly.

In the Reconcile loop and more specifically in

existingPods := &corev1.PodList{}
err = r.client.List(context.TODO(),
&client.ListOptions{
Namespace: request.Namespace,
LabelSelector: labels.SelectorFromSet(lbls),
},
existingPods)
the controller lists the current pods with the expected label(s) and checks their state. If they are in a pending or running state, then they are counted, unless they already have a deletion timestamp set (in which case they will have move to terminating state which will trigger another call to the Reconcile method). (
for _, pod := range existingPods.Items {
if pod.GetObjectMeta().GetDeletionTimestamp() != nil {
continue
}
if pod.Status.Phase == corev1.PodPending || pod.Status.Phase == corev1.PodRunning {
existingPodNames = append(existingPodNames, pod.GetObjectMeta().GetName())
}
}
)

Finally, if the current number of pods does not match the expectation, the controller scales up or down a single pod. Since a pod is created or terminated, the Reconcile loop will be called again, until the desired state is reached.

I hope this clarifies.

@ian-howell
Copy link

I know this is a relatively old project at this point, but there is definitely a race condition here (evidenced by this demo of a slightly modified variant of the code).

@xcoulon I've found a fix, but it isn't perfect. Using predicates, we can prevent the Reconcilerfrom waking up on certain events (in this case, updates). This works, but it prevents new pods from spinning up until after other pods have finished terminating.

	err = c.Watch(
		&source.Kind{Type: &corev1.Pod{}},
		&handler.EnqueueRequestForOwner{
			IsController: true,
			OwnerType:    &clonerv1alpha1.Cloner{},
		},
		predicate.Funcs{
			DeleteFunc: func(_ event.DeleteEvent) bool { return true },
			CreateFunc: func(_ event.CreateEvent) bool { return true },
			UpdateFunc: func(_ event.UpdateEvent) bool { return false },
		},
	)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants