Siyao Meng created HDDS-17420:
---------------------------------

             Summary: S3 writes with an If-None-Match ETag return an unmapped 
HTTP 500, retried by SDKs, while OM does not support S3_IF_NONE_MATCH_ETAG
                 Key: HDDS-17420
                 URL: https://issues.apache.org/jira/browse/HDDS-17420
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: 
CR-10-return-not-implemented-for-if-none-match-etag-before-om-supports-it.patch,
 TestBugCR10PreFinalizedIfNoneMatchS3Error.java

h3. Mechanism
When the OM version cached by {{RpcClient}} is below 
{{OzoneManagerVersion.S3_IF_NONE_MATCH_ETAG}} (104), 
{{RpcClient.checkIfNoneMatchETagSupport}} refuses {{createKeyIfNoneMatch}}, 
{{createStreamKeyIfNoneMatch}} and {{completeMultipartUpload}} with an excluded 
ETag. It throws a plain {{IOException}}. S3 Gateway sends the condition from 
{{ObjectEndpoint.openKeyForPut}}, 
{{ObjectEndpointStreaming.openStreamKeyForPut}} and 
{{ObjectEndpoint.completeMultipartUpload}}, and these paths translate only 
{{OMException}}. S3 Gateway has no exception mapper for {{IOException}}, so 
Jersey passes it to Jetty, which answers HTTP 500 without an S3 error document.

Before HDDS-16622, S3 Gateway rejected a non {{*}} If-None-Match with 400 
InvalidRequest. The ZDU design says S3 Gateway returns clean errors when a 
feature is used before finalization.

h3. Trigger
# OM reports a version below 104 to clients, for example because it runs the 
current software but is not finalized, as during an upgrade before finalization.
# An S3 client sends PutObject, CopyObject or CompleteMultipartUpload with 
{{If-None-Match: "<etag>"}}. {{If-None-Match: *}} is not affected.

Because of HDDS-17140, S3 Gateway keeps the OM version it read first, so this 
continues after finalization until S3 Gateway restarts (from code, not run).

h3. Impact
* The client gets HTTP 500 with no S3 error code. PutObject and CopyObject 
return an empty body, and CompleteMultipartUpload returns a Jetty HTML page.
* The AWS SDK v2 treats it as retryable and sends each request 4 times with its 
default retry policy.
* S3 Gateway logs a WARN with a stack trace from 
{{HttpChannel.handleException}} on every attempt.

The write is refused in every case, as intended. No data is affected.

h3. Reproduction
PASS on unmodified source. [^TestBugCR10PreFinalizedIfNoneMatchS3Error.java] 
({{hadoop-ozone/integration-test-s3}}) starts a mini cluster whose OM begins at 
apparent version ZDU (test only 
{{OMStorage.TESTING_INIT_APPARENT_VERSION_KEY}}), plus one S3 Gateway, and 
sends requests with the AWS SDK v2. A plain PutObject succeeds. With an 
If-None-Match ETag, PutObject, CopyObject and CompleteMultipartUpload each 
report {{HTTP 500, S3 error code null, SDK attempts 4}}, and S3 Gateway counts 
4 failed requests for each. A passing test means the defect is present. A 
variant with datastream enabled ({{hdds.container.ratis.datastream.enabled}} 
and {{ozone.fs.datastream.enabled}} set to true, 
{{ozone.fs.datastream.auto.threshold}} 0MB), which sends PutObject and 
CopyObject through {{ObjectEndpointStreaming}}, gave the same result.

h3. Patch
[^CR-10-return-not-implemented-for-if-none-match-etag-before-om-supports-it.patch],
 against 5f7111a27ea07ce6b7bf5284a4213ebacb65f2ae. It also applies to master at 
e03ba37c41d.

{{RpcClient.checkIfNoneMatchETagSupport}} now throws {{OMException}} with 
{{NOT_SUPPORTED_OPERATION}} and the same message. {{OMException}} extends 
{{IOException}}, so no signature changes. All S3 Gateway paths above already 
translate it through {{S3ErrorTable}}, which maps {{NOT_SUPPORTED_OPERATION}} 
to 501 NotImplemented. The result is an S3 error document that the SDK does not 
retry. {{NOT_SUPPORTED_OPERATION}} is used instead of 
{{NOT_SUPPORTED_OPERATION_PRIOR_FINALIZATION}} because the client cannot tell a 
pre-finalized OM from older OM software. Finalized clusters are unaffected. 
Other version checks in {{RpcClient}} also throw a plain {{IOException}}, but 
they only trigger when OM runs older software than S3 Gateway, which the 
upgrade order rules out. They are not changed here.

Covered by {{TestRpcClient.testIfNoneMatchETagReachesOMOrRejectsOldVersion}}, 
which now also asserts the exception type and result code for every OM version 
below 104. Without the change, 21 of its 22 cases fail. With it:
* the client unit tests (339) and S3 Gateway unit tests (1689) pass;
* {{TestS3SDK}} on a mini cluster passes (249 tests, including the 
If-None-Match ETag cases on a finalized cluster);
* checkstyle is clean.

With the patch, the reproduction and its datastream variant fail because each 
request gets {{HTTP 501, S3 error code NotImplemented, SDK attempts 1}}.

Related, not duplicates: HDDS-16622 (added the check), HDDS-17140 (S3 Gateway 
does not pick up the new OM version after ZDU finalization), HDDS-16994 (wrong 
OM version check for atomic key creation).

Found by code review of the zero downtime upgrade version gating in SCM, OM and 
the datanode write path, as part of the TLA+ verification effort under 
HDDS-15926, on commit 5f7111a27ea07ce6b7bf5284a4213ebacb65f2ae. Checked against 
HDDS issues and apache/ozone pull requests for duplicates before filing. The 
attached patch is a proposal for review. Generated with Specula (Claude Opus 
5.5).



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to