From b4d56d31b4b2ea5499f3f8536ecce05c101c5b27 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Thu, 10 Sep 2026 21:44:33 +0300 Subject: [PATCH] container: support container versioning Closes #380. Signed-off-by: Pavel Karpy --- container/service.proto | 18 +++++++++++++++--- container/types.proto | 13 +++++++++++++ object/service.proto | 18 +++++++++++++++--- proto-docs/container.md | 25 ++++++++++++++++++++++--- proto-docs/object.md | 18 +++++++++++++++--- proto-docs/session.md | 8 ++++++++ proto-docs/status.md | 1 + session/types.proto | 8 ++++++++ status/types.proto | 6 ++++++ 9 files changed, 103 insertions(+), 12 deletions(-) diff --git a/container/service.proto b/container/service.proto index 976c04ed..6fa72b02 100644 --- a/container/service.proto +++ b/container/service.proto @@ -78,6 +78,9 @@ service ContainerService { // - Common failures (SECTION_FAILURE_COMMON); // - **CONTAINER_AWAIT_TIMEOUT** (3075, SECTION_CONTAINER): \ // transaction was sent but not executed within the deadline. + // - **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + // if requester attached container revision he knows and it does not match + // the server's one. rpc SetExtendedACL(SetExtendedACLRequest) returns (SetExtendedACLResponse); // Returns Extended ACL table and signature from `Container` smart contract @@ -115,6 +118,9 @@ service ContainerService { // - Common failures (SECTION_FAILURE_COMMON); // - **CONTAINER_AWAIT_TIMEOUT** (3075, SECTION_CONTAINER): \ // transaction was sent but not executed within the deadline. + // - **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + // if requester attached container revision he knows and it does not match + // the server's one. rpc SetAttribute(SetAttributeRequest) returns (SetAttributeResponse); // Sends transaction calling contract method to remove container attribute, @@ -128,6 +134,9 @@ service ContainerService { // - Common failures (SECTION_FAILURE_COMMON); // - **CONTAINER_AWAIT_TIMEOUT** (3075, SECTION_CONTAINER): \ // transaction was sent but not executed within the deadline. + // - **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + // if requester attached container revision he knows and it does not match + // the server's one. rpc RemoveAttribute(RemoveAttributeRequest) returns (RemoveAttributeResponse); } @@ -336,7 +345,8 @@ message ListResponse { neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; } -// Set Extended ACL +// Set Extended ACL. +// Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. message SetExtendedACLRequest { // Set Extended ACL request body does not have separate `ContainerID` // reference. It will be taken from `EACLTable.container_id` field. @@ -495,7 +505,8 @@ message AnnounceUsedSpaceResponse { neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; } -// Attribute setting request +// Attribute setting request. +// Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. message SetAttributeRequest { // Request payload message. message Body { @@ -568,7 +579,8 @@ message SetAttributeResponse { neo.fs.v2.status.Status status = 1; } -// Attribute removal request +// Attribute removal request. +// Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. message RemoveAttributeRequest { // Request payload message. message Body { diff --git a/container/types.proto b/container/types.proto index 86e3b0ab..dcf95944 100644 --- a/container/types.proto +++ b/container/types.proto @@ -116,4 +116,17 @@ message Container { // Placement policy for the object inside the container neo.fs.v2.netmap.PlacementPolicy placement_policy = 6 [json_name = "placementPolicy"]; + + // Container revision. It increments every time container's properties are + // changed by the owner (or the owner itself is changed). + // + // Do not confuse it with API version: this field describes how many times + // container has been changed since its creation, while API version describes + // proto message format. + // + // It must only be set by storage nodes and must not be filled on the client + // side. The initial revision after a successful container creation call is 0. + // + // Versioned containers are available starting from API v2.27.0. + uint64 revision = 7 [json_name = "revision"]; } diff --git a/object/service.proto b/object/service.proto index b275fbfc..82e4e963 100644 --- a/object/service.proto +++ b/object/service.proto @@ -80,6 +80,9 @@ service ObjectService { // size quota set by user was exceeded; // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // object storage container not found; + // - **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + // if requester attached container revision he knows and it does not match + // the server's one. // - **TOKEN_NOT_FOUND** (4096, SECTION_SESSION): \ // (for trusted object preparation) session private key does not exist or has // been deleted; @@ -112,6 +115,9 @@ service ObjectService { // deleting a locked object is prohibited; // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // object container not found; + // - **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + // if requester attached container revision he knows and it does not match + // the server's one. // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ // provided session token has expired. rpc Delete(DeleteRequest) returns (DeleteResponse); @@ -167,6 +173,9 @@ service ObjectService { // access to operation SEARCH of the object is denied; // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // search container not found; + // - **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + // if requester attached container revision he knows and it does not match + // the server's one. // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ // provided session token has expired. rpc Search(SearchRequest) returns (stream SearchResponse); @@ -393,7 +402,8 @@ message GetResponse { neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; } -// PUT object request +// PUT object request. +// Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. message PutRequest { // PUT request body message Body { @@ -459,7 +469,8 @@ message PutResponse { neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; } -// Object DELETE request +// Object DELETE request. +// Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. message DeleteRequest { // Object DELETE request body message Body { @@ -639,7 +650,8 @@ message SearchResponse { neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; } -// Object SearchV2 request +// Object SearchV2 request. +// Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. message SearchV2Request { // Object Search request body message Body { diff --git a/proto-docs/container.md b/proto-docs/container.md index e9aee69a..9cf58c9c 100644 --- a/proto-docs/container.md +++ b/proto-docs/container.md @@ -167,6 +167,9 @@ Statuses: - Common failures (SECTION_FAILURE_COMMON); - **CONTAINER_AWAIT_TIMEOUT** (3075, SECTION_CONTAINER): \ transaction was sent but not executed within the deadline. +- **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + if requester attached container revision he knows and it does not match + the server's one. | Name | Input | Output | | ---- | ----- | ------ | @@ -216,6 +219,9 @@ Statuses: - Common failures (SECTION_FAILURE_COMMON); - **CONTAINER_AWAIT_TIMEOUT** (3075, SECTION_CONTAINER): \ transaction was sent but not executed within the deadline. +- **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + if requester attached container revision he knows and it does not match + the server's one. | Name | Input | Output | | ---- | ----- | ------ | @@ -233,6 +239,9 @@ Statuses: - Common failures (SECTION_FAILURE_COMMON); - **CONTAINER_AWAIT_TIMEOUT** (3075, SECTION_CONTAINER): \ transaction was sent but not executed within the deadline. +- **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + if requester attached container revision he knows and it does not match + the server's one. | Name | Input | Output | | ---- | ----- | ------ | @@ -571,7 +580,8 @@ returned here to make sure everything has been done as expected. ### Message RemoveAttributeRequest -Attribute removal request +Attribute removal request. +Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. | Field | Type | Label | Description | @@ -637,7 +647,8 @@ Attribute removal response ### Message SetAttributeRequest -Attribute setting request +Attribute setting request. +Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. | Field | Type | Label | Description | @@ -708,7 +719,8 @@ Attribute setting response ### Message SetExtendedACLRequest -Set Extended ACL +Set Extended ACL. +Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. | Field | Type | Label | Description | @@ -784,6 +796,13 @@ of stable-marshalled container message. | basic_acl | [uint32](#uint32) | | `BasicACL` contains access control rules for the owner, system and others groups, as well as permission bits for `BearerToken` and `Extended ACL` | | attributes | [Container.Attribute](#neo.fs.v2.container.Container.Attribute) | repeated | Attributes represent immutable container's meta data | | placement_policy | [neo.fs.v2.netmap.PlacementPolicy](#neo.fs.v2.netmap.PlacementPolicy) | | Placement policy for the object inside the container | +| revision | [uint64](#uint64) | | Container revision. It increments every time container's properties are changed by the owner (or the owner itself is changed). + +Do not confuse it with API version: this field describes how many times container has been changed since its creation, while API version describes proto message format. + +It must only be set by storage nodes and must not be filled on the client side. The initial revision after a successful container creation call is 0. + +Versioned containers are available starting from API v2.27.0. | diff --git a/proto-docs/object.md b/proto-docs/object.md index d6e53769..9229cc25 100644 --- a/proto-docs/object.md +++ b/proto-docs/object.md @@ -170,6 +170,9 @@ Statuses: size quota set by user was exceeded; - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ object storage container not found; +- **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + if requester attached container revision he knows and it does not match + the server's one. - **TOKEN_NOT_FOUND** (4096, SECTION_SESSION): \ (for trusted object preparation) session private key does not exist or has been deleted; @@ -206,6 +209,9 @@ Statuses: deleting a locked object is prohibited; - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ object container not found; +- **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + if requester attached container revision he knows and it does not match + the server's one. - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ provided session token has expired. @@ -269,6 +275,9 @@ Statuses: access to operation SEARCH of the object is denied; - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ search container not found; +- **CONTAINER_REVISION_MISMATCH** (3076, SECTION_CONTAINER): \ + if requester attached container revision he knows and it does not match + the server's one. - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ provided session token has expired. @@ -420,7 +429,8 @@ Statuses: ### Message DeleteRequest -Object DELETE request +Object DELETE request. +Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. | Field | Type | Label | Description | @@ -771,7 +781,8 @@ following steps: ### Message PutRequest -PUT object request +PUT object request. +Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. | Field | Type | Label | Description | @@ -961,7 +972,8 @@ Object Search response body ### Message SearchV2Request -Object SearchV2 request +Object SearchV2 request. +Behaviour can be augmented with __NEOFS__CONTAINER_REVISION x-header. | Field | Type | Label | Description | diff --git a/proto-docs/session.md b/proto-docs/session.md index 7ce058f4..3a1f72d8 100644 --- a/proto-docs/session.md +++ b/proto-docs/session.md @@ -396,6 +396,14 @@ affect system behaviour: how many past epochs the node can look up through. The `value` is string encoded `uint64` in decimal presentation. If set to '0' or not set, only the current epoch will be used. DEPRECATED: header ignored by servers. +* __NEOFS__CONTAINER_REVISION \ + Starting from API v2.27.0 and for a limited set of requests only, requester + can attach container revision to extended headers to ensure container state + is up to date. If server's known revision does not match the requested one, + it must return **CONTAINER_REVISION_MISMATCH** (3076) response status with + no payload. + Value format: a positive base-10 integer counter with no leading zeros. + Check request's description to see if this header is supported. | Field | Type | Label | Description | diff --git a/proto-docs/status.md b/proto-docs/status.md index 3dcc13c1..9a49dd11 100644 --- a/proto-docs/status.md +++ b/proto-docs/status.md @@ -106,6 +106,7 @@ Section of statuses for container-related operations. | EACL_NOT_FOUND | 1 | [**3073**] eACL table not found. | | CONTAINER_LOCKED | 2 | [**3074**] Operation rejected by the container lock. | | CONTAINER_AWAIT_TIMEOUT | 3 | [**3075**] Async container operation timed out. | +| CONTAINER_VERSION_MISMATCH | 4 | [**3076**] Requested operation with container is not in sync: container revision does not meet the server's one. Can be returned only if requester provides container version he has, see requests documentation for more info. | diff --git a/session/types.proto b/session/types.proto index 81b9cc2b..452cd4ab 100644 --- a/session/types.proto +++ b/session/types.proto @@ -203,6 +203,14 @@ message SessionToken { // how many past epochs the node can look up through. The `value` is string // encoded `uint64` in decimal presentation. If set to '0' or not set, only the // current epoch will be used. DEPRECATED: header ignored by servers. +// * __NEOFS__CONTAINER_REVISION \ +// Starting from API v2.27.0 and for a limited set of requests only, requester +// can attach container revision to extended headers to ensure container state +// is up to date. If server's known revision does not match the requested one, +// it must return **CONTAINER_REVISION_MISMATCH** (3076) response status with +// no payload. +// Value format: a positive base-10 integer counter with no leading zeros. +// Check request's description to see if this header is supported. message XHeader { // Key of the X-Header string key = 1 [json_name = "key"]; diff --git a/status/types.proto b/status/types.proto index d993f045..74dbaeb9 100644 --- a/status/types.proto +++ b/status/types.proto @@ -158,6 +158,12 @@ enum Container { // [**3075**] Async container operation timed out. CONTAINER_AWAIT_TIMEOUT = 3; + + // [**3076**] Requested operation with container is not in sync: container + // revision does not meet the server's one. Can be returned only if requester + // provides container version he has, see requests documentation for more + // info. + CONTAINER_VERSION_MISMATCH = 4; } // Section of statuses for session-related operations.