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

Add VolumeSnapshotDelta CSI RPCs #522

Closed
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions csi.proto
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ service Controller {
returns (ControllerGetVolumeResponse) {
option (alpha_method) = true;
}

rpc ListSnapshotDeltas(ListSnapshotDeltasRequest)
returns (ListSnapshotDeltasResponse) {
option (alpha_method) = true;
}
}

service Node {
Expand Down Expand Up @@ -1080,6 +1085,10 @@ message ControllerServiceCapability {
// SINGLE_NODE_SINGLE_WRITER and/or SINGLE_NODE_MULTI_WRITER are
// supported, in order to permit older COs to continue working.
SINGLE_NODE_MULTI_WRITER = 13 [(alpha_enum_value) = true];

// Indicates the SP can compute and retrieve the deltas between
// two snapshots.
LIST_SNAPSHOT_DELTAS = 14 [(alpha_enum_value) = true];
}

Type type = 1;
Expand Down Expand Up @@ -1227,6 +1236,103 @@ message ListSnapshotsResponse {
// An empty string is equal to an unspecified field value.
string next_token = 2;
}

// List the deltas between two snapshots on the storage system
// regardless of how they were created
message ListSnapshotDeltasRequest {
option (alpha_message) = true;

// The ID of the base snapshot handle to use for comparison. If
// not specified, return all changed blocks up to the target
// specified by snapshot_target. This field is OPTIONAL.
string from_snapshot_id = 1;

// The ID of the target snapshot handle to use for comparison. If
// not specified, an error is returned. This field is REQUIRED.
string to_snapshot_id = 2;

// Defines the type of storage. Default to "BLOCK". This field is
// REQUIRED.
enum Mode {
option (alpha_enum) = true;

// BLOCK indicates that the snapshot is of block type.
BLOCK = 0 [(alpha_enum_value) = true];

// FILE indicates that the snapshot is of file type.
FILE = 1 [(alpha_enum_value) = true];
}

// If specified (non-zero value), the Plugin MUST NOT return more
// entries than this number in the response. If the actual number of
// entries is more than this number, the Plugin MUST set `next_token`
// in the response which can be used to get the next page of entries
// in the subsequent `ListSnapshotDeltas` call. This field is
// OPTIONAL. If not specified (zero value), it means there is no
// restriction on the number of entries that can be returned.
// The value of this field MUST NOT be negative.
int32 max_entries = 4;

// A token to specify where to start paginating. Set this field to
// `next_token` returned by a previous `ListSnapshotDeltas` call to
// get the next page of entries. This field is OPTIONAL.
// An empty string is equal to an unspecified field value.
string starting_token = 5;
}
ihcsim marked this conversation as resolved.
Show resolved Hide resolved

message ListSnapshotDeltasResponse {
option (alpha_message) = true;

// The volume size in bytes. This field is OPTIONAL.
uint64 volume_size_bytes = 1;

// This token allows you to get the next page of entries for
// `ListSnapshotDeltas` request. If the number of entries is larger
// than `max_entries`, use the `next_token` as a value for the
// `starting_token` field in the next `ListSnapshotDeltas` request.
// This field is OPTIONAL.
// An empty string is equal to an unspecified field value.
string next_token = 2;

// Changed block deltas between the source and target snapshots. An
// empty list means there is no difference between the two. Leave
// unspecified if the volume isn't of block type. This field is
// OPTIONAL.
repeated BlockSnapshotChangedBlock changed_blocks = 3;
}

message BlockSnapshotChangedBlock {
option (alpha_message) = true;

// The block logical offset on the volume. This field is REQUIRED.
uint64 offset = 1;

// The size of the block in bytes. This field is REQUIRED.
uint64 block_size_bytes = 2;

// The token and other information needed to retrieve the actual
// data block at the given offset. If the provider doesn't support
// token-based data blocks retrieval, this should be left
// unspecified. This field is OPTIONAL.
BlockSnapshotChangedBlockToken token = 3;
}

message BlockSnapshotChangedBlockToken {
option (alpha_message) = true;

// The token to use to retrieve the actual data block at the given
// offset. This field is REQUIRED.
string token = 1;

// Timestamp when the token is issued. This field is REQUIRED.
.google.protobuf.Timestamp issuance_time = 2;

// The TTL of the token in seconds. The expiry time is calculated by
// adding the time of issuance with this value. This field is
// REQUIRED.
int32 ttl_seconds = 3;
}

message ControllerExpandVolumeRequest {
// The ID of the volume to expand. This field is REQUIRED.
string volume_id = 1;
Expand Down
141 changes: 141 additions & 0 deletions spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,11 @@ service Controller {
returns (ControllerGetVolumeResponse) {
option (alpha_method) = true;
}

rpc ListSnapshotDeltas(ListSnapshotDeltasRequest)
returns (ListSnapshotDeltasResponse) {
option (alpha_method) = true;
}
}

service Node {
Expand Down Expand Up @@ -1740,6 +1745,10 @@ message ControllerServiceCapability {
// SINGLE_NODE_SINGLE_WRITER and/or SINGLE_NODE_MULTI_WRITER are
// supported, in order to permit older COs to continue working.
SINGLE_NODE_MULTI_WRITER = 13 [(alpha_enum_value) = true];

// Indicates the SP can compute and retrieve the deltas between
// two snapshots.
LIST_SNAPSHOT_DELTAS = 14 [(alpha_enum_value) = true];
}

Type type = 1;
Expand Down Expand Up @@ -1997,6 +2006,138 @@ The CO MUST implement the specified error recovery behavior when it encounters t
| Invalid `starting_token` | 10 ABORTED | Indicates that `starting_token` is not valid. | Caller SHOULD start the `ListSnapshots` operation again with an empty `starting_token`. |


#### `ListSnapshotDeltas`

**ALPHA FEATURE**

A Controller Plugin MUST implement this RPC call if it has the
`LIST_SNAPSHOT_DELTAS` capability. The Plugin SHALL return the
information about the deltas between the two provided snapshots on the storage
system within the given parameters regardless of how they were created.
`ListSnapshotDeltas` SHALL NOT list deltas of snapshots that are being created
but have not been committed to the storage system. If snapshots are created
and/or deleted while the CO is concurrently paging through `ListSnapshotDeltas`
results, then it is possible that the CO MAY either witness duplicate results in
the list, not witness existing deltas, or both. The CO SHALL NOT expect a
consistent "view" of all snapshot deltas when paging through the snapshot list
via multiple calls to `ListSnapshotDeltas`.

A Controller Plugin MUST NOT attempt to include any raw data blocks in the RPC
response to ensure data plane operations remain outside the scope of CSI. The
response payload MUST contain enough block location metadata for the end users
to retrieve the raw data blocks. The end users are responsbile for devising
their own transport and retrieval mechanism to access the raw data blocks.

Choose a reason for hiding this comment

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

Without an option to transport changed blocks, I am not sure how useful this is. Users would still need to talk to the specific SP directly, and that is exactly what CSI tries to prevent.

Copy link
Author

Choose a reason for hiding this comment

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

At this point, data path remains outside the scope of CSI. The data protection white paper describes a SP-agnostic data movement mechanism here. Our observation is that different providers have different data movement implementation, which makes coming up with generic APIs challenging.


```protobuf
message ListSnapshotDeltasRequest {
option (alpha_message) = true;

// The ID of the base snapshot handle to use for comparison. If
// not specified, return all changed blocks up to the target
// specified by snapshot_target. This field is OPTIONAL.

Choose a reason for hiding this comment

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

snapshot_target is not part of this message, I think you mean to_snapshot_id.

string from_snapshot_id = 1;

// The ID of the target snapshot handle to use for comparison. If
// not specified, an error is returned. This field is REQUIRED.
string to_snapshot_id = 2;

// Defines the type of storage. Default to "BLOCK". This field is
ihcsim marked this conversation as resolved.
Show resolved Hide resolved
// REQUIRED.
enum Mode {
option (alpha_enum) = true;

// BLOCK indicates that the snapshot is of block type.
BLOCK = 0 [(alpha_enum_value) = true];

// FILE indicates that the snapshot is of file type.
FILE = 1 [(alpha_enum_value) = true];
}

// If specified (non-zero value), the Plugin MUST NOT return more
// entries than this number in the response. If the actual number of
// entries is more than this number, the Plugin MUST set `next_token`
// in the response which can be used to get the next page of entries
// in the subsequent `ListSnapshotDeltas` call. This field is
// OPTIONAL. If not specified (zero value), it means there is no
// restriction on the number of entries that can be returned.
// The value of this field MUST NOT be negative.

Choose a reason for hiding this comment

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

if this value MUST NOT be negative, why not use an unsigned int?

Copy link
Author

Choose a reason for hiding this comment

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

This the convention used for other definitions of max_entries in the spec.

Choose a reason for hiding this comment

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

if this value MUST NOT be negative, why not use an unsigned int?

int32 max_entries = 4;

// A token to specify where to start paginating. Set this field to
// `next_token` returned by a previous `ListSnapshotDeltas` call to
// get the next page of entries. This field is OPTIONAL.
// An empty string is equal to an unspecified field value.
string starting_token = 5;
}

message ListSnapshotDeltasResponse {
option (alpha_message) = true;

// The volume size in bytes. This field is OPTIONAL.
uint64 volume_size_bytes = 1;

// This token allows you to get the next page of entries for
// `ListSnapshotDeltas` request. If the number of entries is larger
// than `max_entries`, use the `next_token` as a value for the
// `starting_token` field in the next `ListSnapshotDeltas` request.
// This field is OPTIONAL.
// An empty string is equal to an unspecified field value.
string next_token = 2;

// Changed block deltas between the source and target snapshots. An
// empty list means there is no difference between the two. Leave
// unspecified if the volume isn't of block type. This field is
// OPTIONAL.
repeated BlockSnapshotChangedBlock changed_blocks = 3;
}

message BlockSnapshotChangedBlock {
option (alpha_message) = true;

// The block logical offset on the volume. This field is REQUIRED.
uint64 offset = 1;
Copy link
Member

Choose a reason for hiding this comment

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

.. in bytes?

Copy link
Author

Choose a reason for hiding this comment

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

Not necessarily. E.g., the AWS EBS Direct API uses its own logical offset scheme.


// The size of the block in bytes. This field is REQUIRED.
uint64 block_size_bytes = 2;

// The token and other information needed to retrieve the actual
// data block at the given offset. If the provider doesn't support
// token-based data blocks retrieval, this should be left
// unspecified. This field is OPTIONAL.
BlockSnapshotChangedBlockToken token = 3;
}

message BlockSnapshotChangedBlockToken {
option (alpha_message) = true;

// The token to use to retrieve the actual data block at the given
// offset. This field is REQUIRED.
string token = 1;

// Timestamp when the token is issued. This field is REQUIRED.
.google.protobuf.Timestamp issuance_time = 2;

// The TTL of the token in seconds. The expiry time is calculated by
// adding the time of issuance with this value. This field is
// REQUIRED.
int32 ttl_seconds = 3;
}
```

##### ListSnapshotDeltas Errors

If the plugin is unable to complete the `ListSnapshotDeltas` call successfully,
it MUST return a non-ok gRPC code in the gRPC status. If the conditions defined
below are encountered, the plugin MUST return the specified gRPC error code. The
CO MUST implement the specified error recovery behavior when it encounters the
gRPC error code.

| Condition | gRPC Code | Description | Recovery Behavior |
Copy link
Member

Choose a reason for hiding this comment

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

what if from/to snapshot id are swapped? are SPs required to accept a newer id in "from" and an older it in "to"? or is that considered a bad/invalid request?

Copy link
Author

Choose a reason for hiding this comment

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

The intent is to let the SP do the error handling, and CSI just relays the response back to the end users. Different SP might handle this differently.

|-----------|-----------|-------------|-------------------|
| Target volume snapshot does not exist | 5 NOT_FOUND | Indicates that the target volume snapshot corresponding to the specified `to_snapshot_id` does not exist. | Caller MUST verify that the `to_snapshot_id` is correct and that the volume snapshot is accessible and has not been deleted before retrying with exponential back off. |
Copy link
Contributor

Choose a reason for hiding this comment

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

The base snapshot may not exist as well?

Copy link
Author

Choose a reason for hiding this comment

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

In that case, the delta between the first snapshot since volume creation and the target snapshot will be retrieved.

| Invalid `starting_token` | 10 ABORTED | Indicates that `starting_token` is not valid. | Caller SHOULD start the `ListSnapshotDeltas` operation again with an empty `starting_token`. |


#### `ControllerExpandVolume`

A Controller plugin MUST implement this RPC call if plugin has `EXPAND_VOLUME` controller capability.
Expand Down