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
Ordered initialization for the DRPC condition array #920
Ordered initialization for the DRPC condition array #920
Conversation
b5e7494
to
c7e6792
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to preserve the order? Is this required to fix some issue?
| existingCondition.Status != newCondition.Status || | ||
| existingCondition.ObservedGeneration != newCondition.ObservedGeneration || | ||
| existingCondition.Reason != newCondition.Reason || | ||
| existingCondition.Message != newCondition.Message { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be nice to extract this to conditionsEqual() (like https://pkg.go.dev/bytes#Equal) later.
@nirs I updated the description to include the bugs that this PR fixes. |
|
@BenamarMk you wrote in the bug:
Why do we care about the index of the condition and not using the condition (unique) name? How this change ensures the order of the conditions? |
This line needs a hardcoded index for the PeerReady column to show in
Once the conditions are added to the status, they will not be removed. |
| @@ -107,7 +107,7 @@ func (d *DRPCInstance) RunInitialDeployment() (bool, error) { | |||
|
|
|||
| if homeCluster == "" { | |||
| err := fmt.Errorf("PreferredCluster not set. Placement (%v)", d.userPlacement) | |||
| d.setDRPCCondition(&d.instance.Status.Conditions, rmn.ConditionAvailable, d.instance.Generation, | |||
| addOrUpdateCondition(&d.instance.Status.Conditions, rmn.ConditionAvailable, d.instance.Generation, | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why replace calls to d.setDRPCCondition() with addOrUpdateCondition()?
If this is required refactoring to implement ensureDRPCConditionsInited() it is
better to do in a separate commit. Mixing refactoring and bug fix in the same
commit make it harder to understand the change both in review or 6 month later
when someone tries to understand the history when fixing another issue.
If this change is not required to fix the bug, better not mix it with the fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A fair point.
| return false | ||
| } | ||
|
|
||
| // Initial creation of the DRPC status condition. This will also preserve the ordering of conditions in the array |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be useful to explain here why the ordering of the condition matters, and link to the relevant bug caused by not ensuring the order.
Signed-off-by: Benamar Mekhissi <bmekhiss@ibm.com>
Introduce ordered initialization for the condition array in the DRPC module. This change ensures that the conditions are consistently located in the array, allowing for easier management and maintenance. Additionally, the updates made to the code now properly preserve the order of the conditions, providing more reliable and predictable behavior. Signed-off-by: Benamar Mekhissi <bmekhiss@ibm.com>
c7e6792
to
4dd8e37
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for splitting the commits! it is much more clear now.
| // Initial creation of the DRPC status condition. This will also preserve the ordering of conditions in the array | ||
| func ensureDRPCConditionsInited(conditions *[]metav1.Condition, observedGeneration int64, message string) { | ||
| const DRPCTotalConditions = 2 | ||
| if len(*conditions) == DRPCTotalConditions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This constant is not helping, it does not provide more info and it is not used elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. It is not used anywhere, but it is helping a little bit in two ways:
- It is readable. IOW, now you know what 2 means. And the const tells you that it is really for readability only and the value of 2 is a constant expectation. Meaning, the condition array can't have more than 2 or less than 2.
- Staisfies the linter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we think maximum number of conditions is important and helpful, why not add a module constant? It will make the code nicer even if we don't use it elsewhere. The current form looks like trying to make the linter happy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We absolutely can. Every time you stare at a block of code for a long time, you end up thinking of a better way to rewrite it. This is just the nature of software engineering. At this moment, I will leave it as is for this PR.
| @@ -1759,3 +1761,30 @@ func addOrUpdateCondition(conditions *[]metav1.Condition, conditionType string, | |||
|
|
|||
| return false | |||
| } | |||
|
|
|||
| // Initial creation of the DRPC status condition. This will also preserve the ordering of conditions in the array | |||
| func ensureDRPCConditionsInited(conditions *[]metav1.Condition, observedGeneration int64, message string) { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason to accept a separate observedGeneration and message? Since this is used only
at initialization, we always use drpc.Generation and "Initialization". We could accept a single argument: drpc and use its fields. This makes the function impossible to use incorrectly. Do we really the need to flexibility to call with any generation or message?
| Status: metav1.ConditionTrue, | ||
| LastTransitionTime: time, | ||
| Message: message, | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works only if both conditions are missing, only rmn.ConditionAvailable exists, or both exist. Is it possible that only rmn.ConditionPeerReady exits and it is at index 0 when this is called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While true, this does not occur at present, and not post this PR. The added logic to copy the existing condition in the right order maybe unwanted post this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works only if both conditions are missing, only rmn.ConditionAvailable exists, or both exist. Is it possible that only rmn.ConditionPeerReady exits and it is at index 0 when this is called?
At the start, we want the ConditionAvailable to be at index 0, and ConditionPeerReady to be at index 1. And that's what this patch is doing. So there is no window for ConditionPeerReady to exist before ConditionAvailable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add a comment describing this assumption. Typically you assert() about this so if you assumption is wrong you find it during testing.
| Type: rmn.ConditionAvailable, | ||
| Reason: string(rmn.Initiating), | ||
| ObservedGeneration: observedGeneration, | ||
| Status: metav1.ConditionTrue, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we start with Unknown as the initial condition status, true always gives me the jitters in other parts of the code where we compare at times the status and the generation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like to start with the status set to Unknown as well. But during testing, there was a case where that will break. I don't remember that one. In any case, True won't hurt. Especially, on the initial run of the DRPC instance, the world is good until it isn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add a comment to consider starting with Unknown state. Starting with fake "good" state causes lot of trouble in other case, like restarting minikube env. We get fake status for about 30 seconds from everything and this is very hard to manage. It may not be relevant to drpc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, start positive, until there is a need to switch. Starting with Unknown will just cause more trouble for other clients. The UI is one of them.
|
Merging this for issue progress, please track and close other comments as issues in the future. |
Introduce ordered initialization for condition array, ensuring consistent location. Updates preserve condition order.
Fixes this bug1 and this bug2