-
Notifications
You must be signed in to change notification settings - Fork 588
HDDS-13963. Atomic Create-If-Not-Exists #9332
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
base: master
Are you sure you want to change the base?
HDDS-13963. Atomic Create-If-Not-Exists #9332
Conversation
…not exists" (If-None-Match) semantics.
|
@ivandika3 Could you take a look at whether this API change makes sense? Since rewrite is intended for existing keys, allowing a semantic like “don’t create if existed” can be confusing. On the other hand, adding another flag to the create-key request in the proto feels redundant. Do you have a better suggestion? |
|
Thanks @peterxcli for the patch, I'll take a look when I have time. Involving @sodonnel since he's the original creator of Atomic rewrite. |
I think this depends whether atomic rewrite can be cleanly reused for S3 conditional requests. However after another look, I think adding new FYI, The concept of "generation" was loosely taken from GCP (https://docs.cloud.google.com/storage/docs/request-preconditions) which supports both request preconditions based on generation (GCP specific) and based on ETag (S3 compatible. |
@ivandika3 I opened #9334 and included a design doc. Main idea:
|
|
Thanks @peterxcli, left some comments in #9334, let's discuss the design there. |
…-create-if-not-exists
…-create-if-not-exists
…-create-if-not-exists
…nt tests and protocol definition.
…-create-if-not-exists
…ion handling - Implemented tests for `createKey` and `rewriteKey` methods to validate behavior when using the `EXPECTED_GEN_CREATE_IF_NOT_EXISTS` constant. - Added scenarios for key creation when the key is absent and when it already exists. - Enhanced the `rewriteFailsWhenKeyExists` test to cover cases for both committed and uncommitted keys. - Updated error handling to ensure correct responses for key existence checks.
|
@ivandika3 @sodonnel would you like to review this? this a part of the S3 conditional PUT request design #9334. cc @adoroszlai |
chungen0126
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @peterxcli for working on this. Could you please revert the unnecessary whitespace changes. They are creating a lot of noise in the diff.
I'm concerned that this change might pose a risk of a breaking change. Here is the client-side validation now:
- CLI: The Command Line Interface performs a pre-check on existingKeyGeneration (ensuring it is > 0).
- Java API: The Ozone Java API does not perform this pre-check.
This change introduces a potential regression. While the CLI pre-checks existingKeyGeneration, the Ozone Java API does not. Therefore, modifying the server-side handling of -1 breaks the existing behavior for Java API clients
I also have a question regarding this: Does the If-None-Match request apply to non-S3 interfaces as well?
- If Yes: We need to remove the pre-checks and clarify the usage in the javadocs and the CLI description for put key.
- If No (S3 only): We should probably add a pre-check to the public Ozone Java API to block invalid values and expose a separate, internal interface specifically for S3 use.
MS. will add. |
ivandika3
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@peterxcli Thanks for the patch. Overall LGTM.
Do you think we need a new OzoneManagerVersion for this? If we want to be more conservative, we can fail "If-None-Match" with unsupported exception if OM has not supported it.
| if (existing == null) { | ||
| throw new OMException("Atomic rewrite is not allowed for a new key", KEY_NOT_FOUND); | ||
| } | ||
| if (!toCommit.getExpectedDataGeneration().equals(existing.getUpdateID())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Can reuse expectedGen, also applied to the following exception. expectedGen can be pushed before the if condition.
| if (existing != null) { | ||
| throw new OMException("Key already exists", | ||
| OMException.ResultCodes.KEY_ALREADY_EXISTS); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you help check whether it's better to use a new ResultCodes specific to return S3 412 Precondition Failed? Although KEY_ALREADY_EXISTS does not seem to be handled by S3G currently, it might be safer to add a new result code just in case KEY_ALREADY_EXISTS will be used for other S3 operations.
What changes were proposed in this pull request?
Extends the
expectedDataGenerationlogic in Ozone Manager to support atomic "create-if-not-exists" semantics.EXPECTED_GEN_CREATE_IF_NOT_EXISTS(value:-1) to represent "If-None-Match: *" semantics.expectedDataGenerationis set to-1, thevalidateAtomicRewritelogic (in both Create and Commit phases) strictly enforces that the target key must not exist, throwingKEY_ALREADY_EXISTSif it does.How S3 PUT with If-None-Match Header Leverages This
If-None-Match: *header.expectedDataGeneration = EXPECTED_GEN_CREATE_IF_NOT_EXISTS(-1).RpcClient.rewriteKey().expectedDataGeneration == -1.KEY_ALREADY_EXISTS.-1in open key metadata.expectedDataGeneration == -1from open key.KEY_ALREADY_EXISTS.Race Condition Handling
Using
-1ensures atomicity. If a concurrent write (Client B) commits between Client A's Create and Commit, Client A's commit fails the-1validation check (key now exists), preserving strict create-if-not-exists semantics.What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/HDDS-13963
How was this patch tested?
Added comprehensive unit and integration tests:
Integration Tests (
OzoneRpcClientTests):rewriteFailsWhenKeyExists: Validates three scenarios:-1fails when key is already committed-1succeeds when key is open but not yet committed-1failsOM Request Tests (
TestOMKeyCreateRequest):testCreateKeyExpectedGenCreateIfNotExistsKeyMissing: Create succeeds when key doesn't existtestCreateKeyExpectedGenCreateIfNotExistsKeyAlreadyExists: Create fails withKEY_ALREADY_EXISTSwhen key existstestCreateKeyExpectedGenMismatchReturnsKeyGenerationMismatch: Create fails withKEY_NOT_FOUNDwhen generation mismatchesOM Commit Tests (
TestOMKeyCommitRequest):testAtomicCreateIfNotExistsCommitKeyAbsent: Commit succeeds when key doesn't existtestAtomicCreateIfNotExistsCommitKeyAlreadyExists: Commit fails withKEY_ALREADY_EXISTSwhen key existsAll tests validate both the success paths and error conditions, ensuring atomicity guarantees are preserved.