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

GetCapacityResponse: add maximum_volume_size #470

Merged

Conversation

pohly
Copy link
Contributor

@pohly pohly commented Feb 4, 2021

This new field has a more precisely defined semantic than the existing
available_capacity. It is intended for checking in advance in which
topology a CreateVolume call might succeed. Checking that based
on available_capacity is less precise, for example because
fragmentation might prevent creating a single volume that uses
all available capacity.

The new value has to be returned in a message to allow the caller to
determine whether the value was provided.

Fixes: #432

@pohly
Copy link
Contributor Author

pohly commented Feb 4, 2021

As discussed in the community meeting on 2021-02-03, the semantic of available_capacity is left as ambiguous as it was before. If there ever is an actual need to use it for some specific purpose and that ambiguity becomes a problem, then another change will have to address that.

For issue #432, adding this new field and letting a CO use it when available is sufficient.

@pohly
Copy link
Contributor Author

pohly commented Feb 4, 2021

@bswartz : perhaps you can have a first look whether this reflects what we discussed?

csi.proto Outdated
@@ -947,6 +947,26 @@ message GetCapacityResponse {
// storage. This field is REQUIRED.
// The value of this field MUST NOT be negative.
int64 available_capacity = 1;

// The largest size that could have been used in a CreateVolume call
Copy link
Contributor

Choose a reason for hiding this comment

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

Reword: The largest size that may used in a CreateVolume call

csi.proto Outdated
// This field is OPTIONAL. If a Plugin has no restrictions for the
// size of volumes (for example, linear storage with no fragmentation
// issues and no technical limitation for individual volumes), then
// can leave this field unset and only return available_capacity.
Copy link
Contributor

Choose a reason for hiding this comment

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

Given that we've defined available_capacity very vaguely, and a CO will probably ignore that field, I think a better recommendation here would be to not leave the field blank, but the return the full available capacity, even if that means both fields of this struct have the same value.

Since we don't have a capability bit to go with this feature, I think the presence/absence of this field tells the CO if the SP supports this feature, and therefore we never want to leave it blank, unless the SP doesn't support the feature at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Given that we've defined available_capacity very vaguely, and a CO will probably ignore that field,

No, Kubernetes won't ignore it if that's only field that is set. Some vague value is still better than none.

I think a better recommendation here would be to not leave the field blank, but the return the full available capacity,

But then we need to define what "full capacity" is, right?

We can say "return the same vague value as in available_capacity", but benefit does that provide given that a CO must be prepared to fall back to using available_capacity when maximum_volume_size is unset?

Copy link
Contributor

Choose a reason for hiding this comment

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

No I think we just document that this field should always be the largest possible volume size, and it should never be blank, unless the SP don't support the feature. If the largest possible volume size and the available capacity are the same, that's fine. This allows COs that are only interested in whether a given volume will fit on a given node to answer that question by looking at a single field. The value of the other field could be for sorting the list of possible candidate nodes, if the CO was trying to spread out space consumption, for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Perhaps I should just leave out the sentence with "If a Plugin has no..."? That the field can be unset is already covered by "is OPTIONAL".

We could say "A Plugin SHOULD set this value to the available capacity if it has no additional limitations (for example, fragmentation) that lead to a smaller maximum volume size."

Copy link
Contributor

Choose a reason for hiding this comment

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

I say keep it simple and stick to what the field means and how it will be interpreted by COs. We should not speculate about the reasons why volume size might be limited to a value smaller than the available capacity in the spec. Just say it means the largest volume size which should be successfully created. COs MAY use this value to make decisions about where to schedule volumes,.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. Updated.

This new field has a more precisely defined semantic than the existing
available_capacity. It is intended for checking in advance in which
topology a CreateVolume call might succeed. Checking that based
on available_capacity is less precise, for example because
fragmentation might prevent creating a single volume that uses
all available capacity.

The new value has to be returned in a message to allow the caller to
determine whether the value was provided.
@pohly pohly force-pushed the maximum-volume-size branch from b842b28 to e09188b Compare February 8, 2021 09:47
Copy link
Contributor

@bswartz bswartz left a comment

Choose a reason for hiding this comment

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

/lgtm

@pohly
Copy link
Contributor Author

pohly commented Feb 10, 2021

/assign @saad-ali

These commands don't do anything here, though, do they? 😅

@bswartz
Copy link
Contributor

bswartz commented Feb 11, 2021

Oh IDK what our review process is in this repo. I just do both methods to cover my bases

spec.md Outdated
@@ -1593,6 +1593,24 @@ message GetCapacityResponse {
// storage. This field is REQUIRED.
// The value of this field MUST NOT be negative.
int64 available_capacity = 1;

// The largest size that may be used in a CreateVolume call
Copy link
Member

Choose a reason for hiding this comment

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

This seems to be communicating constraints related to the size of a "contiguous segment" of storage (terms vary across backends, of course) that may be used in a create volume call. Nice idea, and it seems very applicable to a plugin managing e.g. GPT partitions on a block device. I wonder if it could be taken further w/ respect to minimum size requirements -- e.g. perhaps it's not possible to create a storage volume of 1 byte on some backend.

Instead of capturing just the max requirement of such storage segments here, perhaps we should reuse the CapacityRange type to express both min and max requirements. This also would allow the plugin to express that it only has the ability to create volumes of a certain fixed/static size (given the requested parameters, of course), which also might be interesting for some use cases.

Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure about the exact use case where the minimum size is useful because CreateVolume is allowed to create a larger volume than requested, so in contrast to the maximum size it is a "softer" constraint.

But I think it is worthwhile adding nonetheless to minimize churn in the spec and CSI drivers if this turns out to be useful later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added another commit which changes Capacity maximum_volume_size into CapacityRange volume_size. The naming of the CapacityRange is not a perfect fit (required_bytes, limit_bytes), but it is close enough to make sense. I like it slightly better than before, but don't care too much.

@bswartz can you take another look?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't agree that CreateVolume is allowed to create larger volumes than requested. There is a size range, and the upper limit may be larger than the lower limit, or it may be zero (unlimited). If the upper limit is set, though, it would be a spec violation to return a larger size volume. Kubernetes never sets the upper limit, so it's not a problem we frequently notice.

I can see the value of reporting a size range for supported volume size. If a plugin were to report minimum of 1 GiB, and I requested a volume with a max size of 100 MiB, then I could reasonably expect a failure. I also don't like the field names for the exiting CapacityRange structure, and I'm also not sure what to do about it. My best idea is to not attempt to reuse that structure but instead to define two simple Capacity fields. One for max, and one for min. This way we can name them appropriately and the functionality is equivalent to one CapacityRange field.

It's probably worth calling out specifically what we expect to see from the minimum. In some cases it could be a single block (such as 4K). In other cases it might be the minimum the system will allow, which is often hardcoded into the filesystem. And in the case @jdef mentioned it might be the smallest size pre-provisioned chunk of space that's sitting on the shelf.

COs should take care never to set the maximum volume size for CreateVolume() to a size smaller than the minimum. For Kubernetes this will never be an issue because the maximum is always infinity, but for other COs I can see it mattering.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't agree that CreateVolume is allowed to create larger volumes than requested. There is a size range, and the upper limit may be larger than the lower limit, or it may be zero (unlimited). If the upper limit is set, though, it would be a spec violation to return a larger size volume. Kubernetes never sets the upper limit, so it's not a problem we frequently notice.

Indeed, I was thinking only of how Kubernetes uses this. I agree, other COs may do things differently.

It's probably worth calling out specifically what we expect to see from the minimum.

Hmm, isn't it the same abstract rule as for maximum size? "If your CreateVolume call uses a required size smaller than the maximum volume size and a limit larger than the mimimum volume size, then a volume can be created". We decided against mentioning fragmentation as something that affects maximum volume size. Following the same logic we also shouldn't describe in the spec why a driver might report a non-zero minimum size.

My best idea is to not attempt to reuse that structure but instead to define two simple Capacity fields. One for max, and one for min. This way we can name them appropriately and the functionality is equivalent to one CapacityRange field.

Besides naming, an additional benefit is that a driver can leave the minimum size unspecified which may be more natural than "zero" when it has no (known?) minimum size.

I've added YAC (yet another commit 😁 ) with that third alternative. I used the opportunity to explicitly mention the CreateVolumeRequest.capacity_range field.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah I didn't mean to include in the spec the reasons why a minimum might exist, just be specific about how the minimum should be interpreted and perhaps offer examples of valid values. In particular, we should explain if zero is a valid value, because zero has special meaning for the maximum. I might expect that values less than 1 are not okay, and even a 1 byte volume seems strange to me, but perhaps some provider can support it.

These are nitpicks though, I'm actually okay merging the change as-is, if others agree.

The minimum volume size may also be relevant. Another advantage is
that the existing CapacityRange type can be used.
@@ -947,6 +947,39 @@ message GetCapacityResponse {
// storage. This field is REQUIRED.
// The value of this field MUST NOT be negative.
int64 available_capacity = 1;

// The largest size that may be used in a
Copy link

Choose a reason for hiding this comment

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

For volume plugins that don't need to deal with fragmentation issues, I think defining a stricter available_capacity would be beneficial to help us do batch processing of multiple volumes. Right now, we're limited to processing a single volume at a time, which is a major issue for supporting a single workload that requests multiple volumes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Improving the definition of available_capacity was intentionally left out because it seemed to need further discussion.

Copy link

Choose a reason for hiding this comment

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

I meant should we define a new available_capacity field that has a more strict definition?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, probably. But not in this PR 😅

What you want is tracked in #301

Copy link

Choose a reason for hiding this comment

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

Not quite. So we said available_capacity could be interpreted 2 ways: 1) available capacity to provision a single volume and 2) total available capacity as reported by the storage system. Why do we only want to clarify one of the methods that this could be interpreted?

Copy link
Contributor Author

@pohly pohly Mar 1, 2021

Choose a reason for hiding this comment

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

In the last CSI community meeting we talked about whether available_capacity should be clarified and concluded that the wording should be left as-is until there is a specific use-case that needs a stricter definition than the one that is currently in the spec. See (or rather, listen): https://www.youtube.com/watch?list=PLypWp79Fch09Sv7Vo-mHgEo20cGU7oNBc&t=1917&v=ZB0Y05jo7-M&feature=youtu.be

That link points to my summary of the discussion; both @bswartz and @saad-ali confirmed it.

@pohly
Copy link
Contributor Author

pohly commented Feb 24, 2021

@jdef is this now ready to be merged?

When is the CSI 1.4.0 release going to be created? This API change is something that I'd like to use in Kubernetes 1.21, which has a code freeze on Tuesday, March 9th.

@@ -947,6 +947,39 @@ message GetCapacityResponse {
// storage. This field is REQUIRED.
// The value of this field MUST NOT be negative.
int64 available_capacity = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that we have added the new capacity fields for max/min volume size, can we also add comments to this existing field to explain how CO should be using this field?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that is not clear yet. If you have a proposal, then please put it into a PR and we can discuss it.

@saad-ali
Copy link
Member

saad-ali commented Mar 3, 2021

/lgtm
/approve

@jdef Please speak up soon if you have any objections. I want to merge this and cut 1.4 by EOD.

//
// If `volume_capabilities` or `parameters` is
// specified in the request, the Plugin SHALL take those into
// consideration when calculating the maximum volume size of the
Copy link
Member

Choose a reason for hiding this comment

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

s/maximum/minimum/ ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's actually correct here and wrong for maximum_volume_size: the requested minimum volume size must be <= maximum_volume_size and the requested maximum volume size (this case here, aka capacity_range.limit_bytes) >= minimum_volume_size.

I fixed the maximum_volume_size comment.

@jdef
Copy link
Member

jdef commented Mar 3, 2021

Nit: I'm not crazy about a message type that wraps a single int value. There are WKT messages for this. Otherwise lgtm

@saad-ali
Copy link
Member

saad-ali commented Mar 4, 2021

Ack, @pohly can you please make the changes requested by @jdef?

  1. Remove message type that wraps a single int value
  2. s/maximum/minimum/ ?

Once that is done, I'll go ahead and merge this PR and cut a 1.4.0 release

@pohly pohly force-pushed the maximum-volume-size branch 3 times, most recently from 17acd3c to 66b486d Compare March 4, 2021 07:51
CapacityRange volume_size = 2 [(alpha_field) = true];
// where to create volumes. MUST NOT be negative.
google.protobuf.Int64Value maximum_volume_size = 2
[(alpha_field) = true];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The line break is necessary here to pass the length > 72 check.

//
// If `volume_capabilities` or `parameters` is
// specified in the request, the Plugin SHALL take those into
// consideration when calculating the maximum volume size of the
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's actually correct here and wrong for maximum_volume_size: the requested minimum volume size must be <= maximum_volume_size and the requested maximum volume size (this case here, aka capacity_range.limit_bytes) >= minimum_volume_size.

I fixed the maximum_volume_size comment.

Copy link
Contributor Author

@pohly pohly left a comment

Choose a reason for hiding this comment

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

Nit: I'm not crazy about a message type that wraps a single int value. There are WKT messages for this. Otherwise lgtm

Done.

@saad-ali please take another look and merge.

Copy link
Member

@jdef jdef left a comment

Choose a reason for hiding this comment

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

thanks!

@jdef
Copy link
Member

jdef commented Mar 4, 2021

So I was about to merge this, and I realized that we probably don't even need a WKT here. Can't the default value of 0 for either of these new fields translate to "this is field is unset"? We do that elsewhere in the spec, why not here? Or maybe I'm missing something? I'd prefer the simpler form if possible, but if there's a good reason to keep the WKT then OK

@pohly
Copy link
Contributor Author

pohly commented Mar 4, 2021

We do that elsewhere in the spec, why not here?

minimum_volume_size = 0 might be a valid value when the CSI driver has no size restriction. It's probably not a very useful volume if it cannot store data, but still...

It's more obvious for maximum_volume_size = 0: that indicates that the CSI driver completely ran out of capacity, which has to be different from "CSI driver did not provide the information".

@jdef
Copy link
Member

jdef commented Mar 4, 2021 via email

Using CapacityRange in GetCapacityResponse is not a good fit because
the names of its fields is a bit awkward. By adding two fields in
GetCapacityResponse we support the same functionality, plus they can
be left unset separately from each other.
@pohly pohly force-pushed the maximum-volume-size branch from 66b486d to 7a2fcdd Compare March 4, 2021 14:05
@pohly
Copy link
Contributor Author

pohly commented Mar 4, 2021

We already had a comment about "COs MAY...". I added "The Plugin SHOULD..." so now it reads:

  // This field is OPTIONAL. MUST NOT be negative.
  // The Plugin SHOULD provide a value for this field if it has
  // a maximum size for individual volumes and leave it unset
  // otherwise. COs MAY use it to make decision about
  // where to create volumes.
   google.protobuf.Int64Value maximum_volume_size = 2
     [(alpha_field) = true];

@jdef
Copy link
Member

jdef commented Mar 4, 2021

We already had a comment about "COs MAY...". I added "The Plugin SHOULD..." so now it reads:

  // This field is OPTIONAL. MUST NOT be negative.
  // The Plugin SHOULD provide a value for this field if it has
  // a maximum size for individual volumes and leave it unset
  // otherwise. COs MAY use it to make decision about
  // where to create volumes.
   google.protobuf.Int64Value maximum_volume_size = 2
     [(alpha_field) = true];

Thanks.
still LGTM

@saad-ali
Copy link
Member

saad-ali commented Mar 4, 2021

Thank you both @jdef and @pohly. Merging!

@saad-ali saad-ali merged commit 2816e42 into container-storage-interface:master Mar 4, 2021
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

Successfully merging this pull request may close these issues.

GetCapacityResponse: distinguish between maximum volume size and total available capacity
6 participants