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

Proposal: preserving order in strategic merge patch #537

Merged
merged 1 commit into from May 12, 2017

Conversation

mengqiy
Copy link
Member

@mengqiy mengqiy commented Apr 14, 2017

Move from #467, since we want to move strategic merge patch doc out of the api-convention doc. WIP PR

Addressed comments in #467.

cc: @pwittrock @lavalamp @liggitt @apelisse

@mengqiy
Copy link
Member Author

mengqiy commented Apr 24, 2017

Anyone help to review this?

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Apr 24, 2017

## Motivation

The order of lists with a merge strategy is important, because an item in the list may depends on an item before it.
Copy link
Member

@pwittrock pwittrock Apr 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Kubernetes API may apply semantic meaning to the ordering of items within a list, however the strategic merge patch does not keeping the ordering of elements. Ordering has semantic meaning for Environment variables, as later environment variables may reference earlier environment variables, but not the other way around.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example ordering has semantic meaning for environment variables.

I would like to change to this, semantic meaning is not very common.

## Proposed Change

Changes are all in strategic merge patch package.
It will be similar to how we solve the problem of deleting items from a list of primitives.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be similar to how we solve the problem of deleting items from a list of primitives.

The proposed solution is similar to the solution used for deleting elements from lists of primitives.

Changes are all in strategic merge patch package.
It will be similar to how we solve the problem of deleting items from a list of primitives.

Always send a parallel list along with the patch for list:
Copy link
Member

@pwittrock pwittrock Apr 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add to the current patch, a directive containing a list of element keys - either the patch merge key, or for primitives the value. When applying the patch, the server we ensure that the relative ordering of elements matches the directive.

- otherItemN /
```

## Version Skew
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version Skew and Backwards Compatibility


Suppose we define a list of environment variables and we call them
the original environment variables:
```yaml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add space before '`'

- When patching a list of maps, the parallel list contains a list of items. Each item contains only the merge key.
- When patching a list of primitives, the parallel list contains a full list from user's config file.

All the items in the server's live list but not in the parallel list will be append to the end of the parallel list.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this give a simple example

- When patching a list of primitives, the parallel list contains a full list from user's config file.

All the items in the server's live list but not in the parallel list will be append to the end of the parallel list.
The relative order between these appended items are kept.
Copy link
Member

@pwittrock pwittrock Apr 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add these as separate sections:

$setElementOrder may contain elements not present in the patch list

The $setElementOrder value may contain elements that are not present in the patch but present in the list to be merge to reorder the elements as part of the merge. Example where A & B have not changed:

Patch:

$setElementOrder/list:
- A
- B

Live:

list:
- B
- A

Result:

list:
- A
- B

When the list to be merged contains elements not found in $setElementOrder

If the list to be merged contains elements not found in $setElementOrder, they will come after all elements defined in $setElementOrder, but keep their relative ordering.

Example where A & B have been changed:

$setElementOrder/list:
- A
- B
list:
- A
- B
list:
- C
- A
- D
- B
- E

Result:

list:
- A
- B
- C
- D
- E

Explain why this would happen. Explain what happens. Give an example.

When $setElementOrder contains elements not found in the list to be merged

Patch:

$setElementOrder/list:
- C
- A
- B
list:
- A
- B

Live:

list:
- A
- B

Result:

list:
- A
- B

Explain why this would happen. Explain what happens. Given an example


The patch will looks like:
```yaml
$preserveOrderList/env:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$setElementOrder

as later environment variables may reference earlier environment variables,
but not the other way around.

An use case is the environment variables. We don't preserve the order which causes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An use case

One use case


Add to the current patch, a directive containing a list of element keys -
either the patch merge key, or for primitives the value. When applying the patch,
the server we ensure that the relative ordering of elements matches the directive.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the server we ensure

the server ensures


All the items in the server's live list but not in the parallel list will be append to the end of the parallel list.
The relative order between these appended items are kept.
If the relative order of live config in the server is different from the order of the parallel list,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the relative order of live config

If the relative order of the live config

All the items in the server's live list but not in the parallel list will be append to the end of the parallel list.
The relative order between these appended items are kept.
If the relative order of live config in the server is different from the order of the parallel list,
user's patch will always override the order in the server.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user's patch will al

the user's patch will al

The new patch has one additional parallel list which will be dropped by the old server.

The logic for old version patch is still kept,
so the patch from any arbitrary old clients used to work before will continue to function.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the patch from any arbitrary old clients used to work before will continue to function.

The new directive is optional. Patch requests without the directive will retain the behavior from previous releases. This will ensure backward compatibility with old clients.

Then the server appends two finalizers and reorder the list:

```yaml
finalizers:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really helpful example. Good job.

@pwittrock
Copy link
Member

Minor nits on wording. Otherwise LGTM.

@mengqiy
Copy link
Member Author

mengqiy commented May 1, 2017

Updated.

@pwittrock
Copy link
Member

This looks good to me.

@mengqiy
Copy link
Member Author

mengqiy commented May 1, 2017

Squashed commits.

The new patch has one additional parallel list which will be dropped by the old server.

The new directive is optional.
Patch requests without the directive will retain the behavior from previous releases.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should do something better

- E
```

### When `$setElementOrder` contains elements not found in the list to be merged
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a section for

When $setElementOrder is not present and patching a list.

```


# Alternative Considered
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another alternative, is to send the mergeKeys as empty elements in the list, and have the server maintain the order of the provided items. In this case, we don't need to introduce any new directives, and the patch should be totally compatible. The patch list would always be reordered to come before all elements missing from the patch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking on this more, if we did that, we might end up creating elements that we just want to order - e.g. if we want to modify "A", and send "A, B, C" as the main list to keep ordering, "C" would get recreated if it was deleted or missing, which we would not want.

@mengqiy
Copy link
Member Author

mengqiy commented May 2, 2017

Updated. PTAL.

Changes are all in strategic merge patch package.
The proposed solution is similar to the solution used for deleting elements from lists of primitives.

Add to the current patch, a directive ($setElementOrder) containing a list of element keys -
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not every list wants to keep ordering, whether we will consider add a directive to type definition?

Copy link
Member Author

@mengqiy mengqiy May 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a directive to type definition

I don't understand what you mean here. Can you give me an example?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adohe I think you mean that the ordering is not semantically important for every list, and should we add a comment tag to the field definition indicating whether order has semantic meaning. (e.g. The containers list in a PodTemplate does not have semantically meaningful order).

I think this would be be interesting to do in a follow up. Since this is totally optional and sent by the client, the client should decide whether it wants to control the order or not for a given patch (yes it should want to do so)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pwittrock yep, that's exactly what I mean. We could do this in a follow up and I am just wondering how the client can decide whether or not to keep list order.

The proposed solution is similar to the solution used for deleting elements from lists of primitives.

Add to the current patch, a directive ($setElementOrder) containing a list of element keys -
either the patch merge key, or for primitives the value. When applying the patch,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any consideration about multiple key as merge key?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mergeKey is a key-value pair in the map. If we are not using any special format, I think new additional merge keys will simply be new key-value pair in the map.

@pwittrock
Copy link
Member

@fabianofranz @liggitt I will be merging this today if there are no other comments.

For changes and deletions, the server will just merge the change without sorting them.
So the items in the list will retain the order.

For additions, the items will be appended to the end of the list.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the server has [A,B,C]
and the client sends [X,A,B,C]
always appending to the end has this counter-intuitive result: [A,B,C,X]

Are old clients known to scramble the order when computing the patch, or was the scrambling happening server-side?

I would have expected the order of two items to be determined by something like the following:

  • relative order in the $setElementOrder if both items are present
  • else relative order in the patch if both items are present
  • else relative order in the server-side list if both items are present
  • else append to the end

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are old clients known to scramble the order when computing the patch, or was the scrambling happening server-side?

Both. We sort the list by mergeKey when diffing and merging in current strategic merge patch implementation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@liggitt What should the result be when applying patch [c,b,a] to server's list [a,x,b,y,c]?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot find a way to keep relative order of (a,x) and (x,b) at the same time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@liggitt Your later comment addresses this I think - all items in the list must appear in the orderList

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was expecting something like https://play.golang.org/p/tJLW1Zj0h7 where the directive is used if present, otherwise the relative order in the patch list is honored for item pairs which are both present, and finally the relative server-side order is honored.

that is less compelling if old clients sent randomly ordered lists

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot. This is super helpful.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

### When the list to be merged contains elements not found in `$setElementOrder`

If the list to be merged contains elements not found in $setElementOrder,
they will come after all elements defined in $setElementOrder, but keep their relative ordering.
Copy link
Member

@liggitt liggitt May 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the patch contains $setElementOrder, it seems like an error for $setElementOrder to be missing items that are present in the list (or to have items in a different order than the list)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like an error for $setElementOrder to be missing items that are present in the list

I'm OK to fail the operation. I will update it soon.

or to have items in a different order than the list

Do you mean we should always verify the order in patch list and the order in $setElementOrder match before merging?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @liggitt was suggesting both are required. I think that is probably fine.

but still be fully backward compatible.

### kubectl
If an old kubectl sends a old patch to a new server, the server will not preserve the order which behave as an old server.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we not do better here by honoring relative order of items in the patch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above #537 (comment), a patch sent by old kubectl doesn't has any useful order in it. Because it has been sorted by mergeKey.
What should we do in this case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@liggitt I was thinking about this as well.

I think you are saying, can you just use the ordering in the patch list instead of sending another directive.

The issue here is: If you have a list of [a,b,c] and want to patch b you would either send a patch [b] or send a patch [a,b,c]. The former lacks the ordering, and the latter will recreate [a,c] if they were deleted from the server.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it removes the need for the separate order-indicating directive, but it seems really counter-intuitive to ignore relative order in the list

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@liggitt Now sure how much of an issue you think this is. I think it is an edge case, but one that be annoying to track down if it is encountered.

@mengqiy
Copy link
Member Author

mengqiy commented May 4, 2017

This change is Reviewable

@mengqiy
Copy link
Member Author

mengqiy commented May 6, 2017

PTAL

@mengqiy
Copy link
Member Author

mengqiy commented May 12, 2017

Squashed commits.

@pwittrock
Copy link
Member

@LiGgit merging this since it looks like you comments have been addressed

@pwittrock pwittrock merged commit ab60a1d into kubernetes:master May 12, 2017
@mengqiy mengqiy deleted the preserve_order_proposal branch May 22, 2017 17:31
danehans pushed a commit to danehans/community that referenced this pull request Jul 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants