peterxcli opened a new pull request, #11139: URL: https://github.com/apache/ozone/pull/11139
## What changes were proposed in this pull request? S3 `CopyObject` with the same source and destination key plus `x-amz-metadata-directive: REPLACE` is the mechanism AWS documents for updating an object's metadata in place. Today the S3 Gateway serves it by streaming every byte of the object from the datanodes through the gateway and writing a brand new key (`ObjectEndpoint#copyObject`), which is pure waste for a metadata-only change. This PR turns that request into a metadata-only update in the Ozone Manager. **New OM write request `SetObjectMetadata`** (`S3SetObjectMetadataRequest` plus an FSO variant) replaces the custom metadata and the tag set of an existing key under `BUCKET_LOCK`: * System-managed metadata entries (`ETag`, GDPR flag/secret/algorithm, `hsyncClientId`) are always carried over from the existing key server-side, so a metadata update can never corrupt the stored content ETag or make a GDPR key unreadable. The S3 Gateway is never trusted to filter these. ETag preservation is required by AWS semantics: the ETag reflects only changes to the object's contents, not its metadata. * `keyLocationVersions`, `dataSize`, replication config and `FileEncryptionInfo` are untouched. * `modificationTime` is updated (stamped in `preExecute`) because AWS `CopyObject` updates `LastModified`. This deliberately differs from `PutObjectTagging`, which keeps the modification time. * Tags are replaced in the same atomic request, so `x-amz-tagging-directive` keeps working for self-copies. **Proto / plumbing.** New `SetObjectMetadata` cmdType with request and response messages; metadata and tags travel in the existing `KeyArgs` fields. Classified as a write in `OmUtils`, registered in `BucketLayoutAwareOMKeyRequestFactory` and `OzoneManagerRatisUtils`, with a new `OMAction` audit value and OM metrics. **Version gating.** New `OzoneManagerVersion.SET_OBJECT_METADATA` checked client-side in `RpcClient`, and a new `OMLayoutFeature.SET_OBJECT_METADATA` with `@DisallowedUntilLayoutVersion` on `preExecute` so a pre-finalized cluster rejects the new cmdType. There is no on-disk schema change. **Client API.** `OzoneBucket#setObjectMetadata` through `ClientProtocol`, `RpcClient` and `OzoneManagerProtocolClientSideTranslatorPB`. **S3 Gateway.** In `ObjectEndpoint#copyObject`, a self-copy with directive `REPLACE` — after the copy-source preconditions are evaluated, when the storage class does not imply a replication change and no write preconditions are present — calls `setObjectMetadata` and returns the stored ETag with the new `LastModified`. When the OM does not support the API (older OM, pre-finalized cluster, or an FSO directory) it falls back silently to the existing byte-copy path, so mixed-version clusters keep working. ### Why a new cmdType instead of extending PutObjectTagging Any OM from version 9 onwards already accepts `PutObjectTagging`, and `metadata` is an existing field of `KeyArgs`, not an unknown one. An old OM receiving a tagging request that carried metadata would therefore replace the tag set, silently ignore the metadata and return `OK` — telling the client the metadata update succeeded when it had not happened. A distinct cmdType makes an old OM reject the request cleanly, which is exactly what the gateway's fallback keys off. Reusing the message would also require an explicit `replaceMetadata` flag, because "clear all metadata" and "leave metadata alone" are both the empty repeated field. ### Included refactor The third commit (`HDDS-16318. Deduplicate in-place key update requests and responses`) extracts the duplication this feature would otherwise have added to. `PutObjectTagging`, `DeleteObjectTagging` and `SetObjectMetadata` were near-verbatim copies of one request skeleton in both bucket layouts — `S3DeleteObjectTaggingRequest` differed from its Put sibling in eight substantive lines — backed by six byte-identical response classes. A shared `OMKeyInfoUpdateRequest` base now owns the lock / validate / mutate / cache / audit skeleton and both layout resolutions, and a shared `OMKeyInfoUpdateResponse(WithFSO)` replaces the six response classes, following the pattern `S3BucketTaggingRequestBase` already established for bucket tagging. Net 907 lines removed. That commit also normalizes two inconsistencies the duplication had let drift, in both cases to the object-store behaviour: the FSO tagging variants never called `markForAudit` (so `PutObjectTagging` and `DeleteObjectTagging` on an FSO bucket produced no audit record), and they logged client request failures without the `OMClientRequestUtils.shouldLogClientRequestFailure` guard. `OMKeySetTimesResponse` looks like more of the same but is genuinely different — it also updates FSO directories and declares `DIRECTORY_TABLE` cleanup — so it is left alone. The commit is self-contained and touches no proto, no client API and no S3G code, so it can be split into its own Jira and PR if reviewers prefer. ### Measured effect S3 Gateway side only, driven through the real `ObjectEndpoint` against the in-memory client stub, self-copy with metadata directive `REPLACE`, median of 15 iterations with ABBA ordering (Apple M4, JDK 21). The "before" arm is produced by making the OM reject `SetObjectMetadata`, which is exactly the fallback path this PR adds: | object size | full copy (today) | metadata only | speedup | |---|---|---|---| | 1 MiB | 2.36 ms | 0.51 ms | 4.6x | | 8 MiB | 12.23 ms | 0.35 ms | 34.9x | | 32 MiB | 44.79 ms | 0.35 ms | 128.9x | | 64 MiB | 89.81 ms | 0.37 ms | 244.9x | The current cost is linear in object size, roughly 1.4 ms of gateway CPU per MiB; the metadata-only update is constant at about 0.35 ms. Each measurement was probed to confirm which path it actually took (the "before" arm issued 15 `createKey` calls and no `setObjectMetadata`, the "after" arm the reverse), so neither arm silently collapsed into the other. These numbers are a lower bound rather than cluster numbers: the harness has no datanode I/O, no replication and no RPC. On a real cluster the same operation additionally stops reading N bytes from datanodes and writing N x replication bytes back, so a 64 MiB self-copy also avoids roughly 192 MiB of replicated writes. ## What is the link to the Apache JIRA https://issues.apache.org/jira/browse/HDDS-16318 Related: HDDS-569 proposed a metadata-only copy in 2018. ## How was this patch tested? * New OM request unit tests for both layouts (`TestS3SetObjectMetadataRequest`, `TestS3SetObjectMetadataRequestWithFSO`): metadata replaced, reserved system keys preserved even when the request tries to override them, modification time updated, update ID set, and FSO directories rejected. * New `TestOMKeyInfoUpdateResponse(WithFSO)` covering the shared response for both layouts, replacing the six deleted response test classes. * All existing tagging request tests pass unchanged against the refactored classes (`TestS3PutObjectTaggingRequest(WithFSO)`, `TestS3DeleteObjectTaggingRequest(WithFSO)`), together with `TestBucketLayoutAwareOMKeyFactory`, which reflectively instantiates every registered request class in all three bucket layouts, and `TestCleanupTableInfo`, which reflects over the whole response package. * s3gateway `TestObjectPut` (50 tests), including three new ones: the self-copy is served as a metadata-only update with no key rewrite and the ETag preserved; tags survive the update under the default `COPY` tagging directive; and the copy falls back to the byte-copy path when the OM rejects the API, covering the mixed-version case. * `AbstractS3SDKV2Tests#testCopyObjectToSelfWithMetadataReplace` exercises the path end to end through the real AWS SDK v2 client, strengthened here to assert the ETag is preserved. * New robot test case in `objectcopy.robot` asserting the metadata is replaced in place and the object content is unchanged. * `checkstyle.sh` and `rat.sh` are clean. Generated-by: Claude Code (claude-fable-5) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
