This is an automated email from the ASF dual-hosted git repository.

weichiu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new c6da3cf1abd HDDS-13963. Atomic Create-If-Not-Exists (#9332)
c6da3cf1abd is described below

commit c6da3cf1abd5846db7c28b7ef3b63e87f28c2790
Author: Peter Lee <[email protected]>
AuthorDate: Sat Mar 28 01:35:53 2026 +0800

    HDDS-13963. Atomic Create-If-Not-Exists (#9332)
---
 .../java/org/apache/hadoop/ozone/OzoneConsts.java  |  2 +
 .../apache/hadoop/ozone/OzoneManagerVersion.java   |  3 +
 .../apache/hadoop/ozone/client/OzoneBucket.java    |  6 +-
 .../ozone/client/protocol/ClientProtocol.java      |  2 +-
 .../apache/hadoop/ozone/client/rpc/RpcClient.java  |  7 +-
 .../hadoop/ozone/om/exceptions/OMException.java    |  4 +-
 ...OzoneManagerProtocolClientSideTranslatorPB.java |  2 +-
 .../ozone/client/rpc/OzoneRpcClientTests.java      | 21 ++++++
 .../ozone/om/request/key/OMKeyCommitRequest.java   | 33 ++++++---
 .../ozone/om/request/key/OMKeyCreateRequest.java   | 35 ++++++---
 .../om/request/key/TestOMKeyCommitRequest.java     | 73 ++++++++++++++++++-
 .../om/request/key/TestOMKeyCreateRequest.java     | 84 ++++++++++++++++++++++
 12 files changed, 244 insertions(+), 28 deletions(-)

diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
index 3bd8388f950..151022823a1 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
@@ -318,6 +318,8 @@ public final class OzoneConsts {
   public static final String TENANT = "tenant";
   public static final String USER_PREFIX = "userPrefix";
   public static final String REWRITE_GENERATION = "rewriteGeneration";
+  /** Sentinel generation used to request atomic create-if-not-exists(put if 
absent) semantics. */
+  public static final long EXPECTED_GEN_CREATE_IF_NOT_EXISTS = -1L;
   public static final String FROM_SNAPSHOT = "fromSnapshot";
   public static final String TO_SNAPSHOT = "toSnapshot";
   public static final String TOKEN = "token";
diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneManagerVersion.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneManagerVersion.java
index 41cf8ab2856..7d3f8629f0e 100644
--- 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneManagerVersion.java
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneManagerVersion.java
@@ -54,6 +54,9 @@ public enum OzoneManagerVersion implements ComponentVersion {
 
   S3_LIST_MULTIPART_UPLOADS_PAGINATION(11,
       "OzoneManager version that supports S3 list multipart uploads API with 
pagination"),
+
+  ATOMIC_CREATE_IF_NOT_EXISTS(12,
+      "OzoneManager version that supports explicit create-if-not-exists key 
semantics"),
     
   FUTURE_VERSION(-1, "Used internally in the client when the server side is "
       + " newer and an unknown server version has arrived to the client.");
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/OzoneBucket.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/OzoneBucket.java
index 75d7d82c5e1..09cd189e8e4 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/OzoneBucket.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/OzoneBucket.java
@@ -508,7 +508,7 @@ public OzoneOutputStream createKey(String key, long size,
    *
    * @param keyName Existing key to rewrite. This must exist in the bucket.
    * @param size The size of the new key
-   * @param existingKeyGeneration The generation of the existing key which is 
checked for changes at key create
+   * @param existingKeyGeneration The positive generation of the existing key 
which is checked for changes at key create
    *                              and commit time.
    * @param replicationConfig The replication configuration for the key to be 
rewritten.
    * @param metadata custom key value metadata
@@ -1047,8 +1047,8 @@ public List<OzoneFileStatus> listStatus(String keyName, 
boolean recursive,
    *
    * @param prefix Optional string to filter for the selected keys.
    */
-  public OzoneMultipartUploadList listMultipartUploads(String prefix, 
-      String keyMarker, String uploadIdMarker, int maxUploads) 
+  public OzoneMultipartUploadList listMultipartUploads(String prefix,
+      String keyMarker, String uploadIdMarker, int maxUploads)
       throws IOException {
     return proxy.listMultipartUploads(volumeName, getName(), prefix, 
keyMarker, uploadIdMarker, maxUploads);
   }
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
index e3a57589634..24ddd782cd2 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
@@ -368,7 +368,7 @@ OzoneOutputStream createKey(String volumeName, String 
bucketName,
    * @param bucketName Name of the Bucket
    * @param keyName Existing key to rewrite. This must exist in the bucket.
    * @param size The size of the new key
-   * @param existingKeyGeneration The generation of the existing key which is 
checked for changes at key create
+   * @param existingKeyGeneration The positive generation of the existing key 
which is checked for changes at key create
    *                              and commit time.
    * @param replicationConfig The replication configuration for the key to be 
rewritten.
    * @param metadata custom key value metadata
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
index 1702e433b32..641a63a28f9 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
@@ -677,7 +677,7 @@ public void createBucket(
       builder.setDefaultReplicationConfig(defaultReplicationConfig);
     }
 
-    String replicationType = defaultReplicationConfig == null 
+    String replicationType = defaultReplicationConfig == null
         ? "server-side default replication type"
         : defaultReplicationConfig.getType().toString();
 
@@ -1317,7 +1317,7 @@ public List<OzoneBucket> listBuckets(String volumeName, 
String bucketPrefix,
     List<OmBucketInfo> buckets = ozoneManagerClient.listBuckets(
         volumeName, prevBucket, bucketPrefix, maxListResult, hasSnapshot);
 
-    return buckets.stream().map(bucket -> 
+    return buckets.stream().map(bucket ->
             OzoneBucket.newBuilder(conf, this)
                 .setVolumeName(bucket.getVolumeName())
                 .setName(bucket.getBucketName())
@@ -1408,6 +1408,9 @@ public OzoneOutputStream rewriteKey(String volumeName, 
String bucketName, String
     if (omVersion.compareTo(OzoneManagerVersion.ATOMIC_REWRITE_KEY) < 0) {
       throw new IOException("OzoneManager does not support atomic key 
rewrite.");
     }
+    Preconditions.checkArgument(existingKeyGeneration > 0,
+        "existingKeyGeneration must be positive, but was %s",
+        existingKeyGeneration);
 
     createKeyPreChecks(volumeName, bucketName, keyName, replicationConfig);
 
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
index 596eb127656..2b5b5559f92 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
@@ -208,7 +208,7 @@ public enum ResultCodes {
     USER_MISMATCH, // Error code when requested user name passed is different
     // from remote user.
 
-    INVALID_PART, // When part name is not found or not matching with partname 
+    INVALID_PART, // When part name is not found or not matching with partname
     // in OM MPU partInfo.
 
     INVALID_PART_ORDER, // When list of parts mentioned to complete MPU are not
@@ -267,7 +267,7 @@ public enum ResultCodes {
     UNAUTHORIZED,
 
     S3_SECRET_ALREADY_EXISTS,
-    
+
     INVALID_PATH,
     TOO_MANY_BUCKETS,
     KEY_UNDER_LEASE_RECOVERY,
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
index 6960c11aaaa..20f0babab82 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
@@ -1255,7 +1255,7 @@ public String createSnapshot(String volumeName,
     if (!StringUtils.isBlank(snapshotName)) {
       requestBuilder.setSnapshotName(snapshotName);
     }
-      
+
     final OMRequest omRequest = createOMRequest(Type.CreateSnapshot)
         .setCreateSnapshotRequest(requestBuilder)
         .build();
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/OzoneRpcClientTests.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/OzoneRpcClientTests.java
index 61f3d230812..378316c6b15 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/OzoneRpcClientTests.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/OzoneRpcClientTests.java
@@ -36,6 +36,7 @@
 import static 
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SNAPSHOT_DELETING_SERVICE_INTERVAL;
 import static org.apache.hadoop.ozone.OzoneConsts.DEFAULT_OM_UPDATE_ID;
 import static org.apache.hadoop.ozone.OzoneConsts.ETAG;
+import static 
org.apache.hadoop.ozone.OzoneConsts.EXPECTED_GEN_CREATE_IF_NOT_EXISTS;
 import static org.apache.hadoop.ozone.OzoneConsts.GB;
 import static org.apache.hadoop.ozone.OzoneConsts.MD5_HASH;
 import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
@@ -1435,6 +1436,26 @@ void 
rewriteFailsDueToOutdatedGenerationAtCommit(BucketLayout layout) throws IOE
     assertUnchanged(keyInfo, ozoneManager.lookupKey(keyArgs));
   }
 
+  @ParameterizedTest
+  @EnumSource
+  void rewriteRejectsNonPositiveGeneration(BucketLayout layout)
+      throws IOException {
+    checkFeatureEnable(OzoneManagerVersion.ATOMIC_REWRITE_KEY);
+    OzoneBucket bucket = createBucket(layout);
+    OzoneKeyDetails key1Details = createTestKey(bucket, "key1", 
"value".getBytes(UTF_8));
+    IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+        () -> {
+        bucket.rewriteKey("key2",
+            1024,
+            EXPECTED_GEN_CREATE_IF_NOT_EXISTS,
+            
RatisReplicationConfig.getInstance(HddsProtos.ReplicationFactor.ONE),
+            singletonMap("key", "value"));
+        });
+
+    assertThat(e).hasMessageContaining("existingKeyGeneration must be 
positive");
+    assertKeyContent(bucket, key1Details.getName(), "value".getBytes(UTF_8));
+  }
+
   @ParameterizedTest
   @EnumSource
   void cannotRewriteDeletedKey(BucketLayout layout) throws IOException {
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyCommitRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyCommitRequest.java
index be0935d909d..492d698db99 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyCommitRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyCommitRequest.java
@@ -94,7 +94,13 @@ public OMRequest preExecute(OzoneManager ozoneManager) 
throws IOException {
     KeyArgs keyArgs = commitKeyRequest.getKeyArgs();
 
     if (keyArgs.hasExpectedDataGeneration()) {
-      ozoneManager.checkFeatureEnabled(OzoneManagerVersion.ATOMIC_REWRITE_KEY);
+      if (keyArgs.getExpectedDataGeneration()
+          == OzoneConsts.EXPECTED_GEN_CREATE_IF_NOT_EXISTS) {
+        ozoneManager.checkFeatureEnabled(
+            OzoneManagerVersion.ATOMIC_CREATE_IF_NOT_EXISTS);
+      } else {
+        
ozoneManager.checkFeatureEnabled(OzoneManagerVersion.ATOMIC_REWRITE_KEY);
+      }
     }
 
     if (ozoneManager.getConfig().isKeyNameCharacterCheckEnabled()) {
@@ -616,14 +622,23 @@ protected void validateAtomicRewrite(OmKeyInfo existing, 
OmKeyInfo toCommit, Map
     if (toCommit.getExpectedDataGeneration() != null) {
       // These values are not passed in the request keyArgs, so add them into 
the auditMap if they are present
       // in the open key entry.
-      auditMap.put(OzoneConsts.REWRITE_GENERATION, 
String.valueOf(toCommit.getExpectedDataGeneration()));
-      if (existing == null) {
-        throw new OMException("Atomic rewrite is not allowed for a new key", 
KEY_NOT_FOUND);
-      }
-      if 
(!toCommit.getExpectedDataGeneration().equals(existing.getUpdateID())) {
-        throw new OMException("Cannot commit as current generation (" + 
existing.getUpdateID() +
-            ") does not match the expected generation to rewrite (" + 
toCommit.getExpectedDataGeneration() + ")",
-            KEY_NOT_FOUND);
+      Long expectedGen = toCommit.getExpectedDataGeneration();
+      auditMap.put(OzoneConsts.REWRITE_GENERATION, 
String.valueOf(expectedGen));
+
+      if (expectedGen == OzoneConsts.EXPECTED_GEN_CREATE_IF_NOT_EXISTS) {
+        if (existing != null) {
+          throw new OMException("Key already exists",
+              OMException.ResultCodes.KEY_ALREADY_EXISTS);
+        }
+      } else {
+        if (existing == null) {
+          throw new OMException("Atomic rewrite is not allowed for a new key", 
KEY_NOT_FOUND);
+        }
+        if (expectedGen != existing.getUpdateID()) {
+          throw new OMException("Cannot commit as current generation (" + 
existing.getUpdateID() +
+              ") does not match the expected generation to rewrite (" + 
expectedGen + ")",
+              KEY_NOT_FOUND);
+        }
       }
     }
   }
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyCreateRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyCreateRequest.java
index d34320ecb8d..1298ff7426f 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyCreateRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyCreateRequest.java
@@ -36,6 +36,7 @@
 import org.apache.hadoop.hdds.scm.container.common.helpers.ExcludeList;
 import org.apache.hadoop.hdds.utils.UniqueId;
 import org.apache.hadoop.ozone.OmUtils;
+import org.apache.hadoop.ozone.OzoneConsts;
 import org.apache.hadoop.ozone.OzoneManagerVersion;
 import org.apache.hadoop.ozone.audit.OMAction;
 import org.apache.hadoop.ozone.om.OMMetadataManager;
@@ -95,7 +96,13 @@ public OMRequest preExecute(OzoneManager ozoneManager) 
throws IOException {
     final OMPerformanceMetrics perfMetrics = ozoneManager.getPerfMetrics();
 
     if (keyArgs.hasExpectedDataGeneration()) {
-      ozoneManager.checkFeatureEnabled(OzoneManagerVersion.ATOMIC_REWRITE_KEY);
+      if (keyArgs.getExpectedDataGeneration()
+          == OzoneConsts.EXPECTED_GEN_CREATE_IF_NOT_EXISTS) {
+        ozoneManager.checkFeatureEnabled(
+            OzoneManagerVersion.ATOMIC_CREATE_IF_NOT_EXISTS);
+      } else {
+        
ozoneManager.checkFeatureEnabled(OzoneManagerVersion.ATOMIC_REWRITE_KEY);
+      }
     }
 
     OmUtils.verifyKeyNameWithSnapshotReservedWord(keyArgs.getKeyName());
@@ -189,7 +196,7 @@ public OMRequest preExecute(OzoneManager ozoneManager) 
throws IOException {
 
     KeyArgs.Builder finalNewKeyArgs = newKeyArgs;
     KeyArgs resolvedKeyArgs =
-        
captureLatencyNs(perfMetrics.getCreateKeyResolveBucketAndAclCheckLatencyNs(), 
+        
captureLatencyNs(perfMetrics.getCreateKeyResolveBucketAndAclCheckLatencyNs(),
             () -> resolveBucketAndCheckKeyAcls(finalNewKeyArgs.build(), 
ozoneManager,
             IAccessAuthorizer.ACLType.CREATE));
     newCreateKeyRequest =
@@ -369,7 +376,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager 
ozoneManager, Execut
       } else {
         perfMetrics.addCreateKeyFailureLatencyNs(createKeyLatency);
       }
-      
+
       if (acquireLock) {
         mergeOmLockDetails(ozoneLockStrategy
             .releaseWriteLock(omMetadataManager, volumeName,
@@ -471,12 +478,22 @@ public static OMRequest 
blockCreateKeyWithBucketLayoutFromOldClient(
   protected void validateAtomicRewrite(OmKeyInfo dbKeyInfo, KeyArgs keyArgs)
       throws OMException {
     if (keyArgs.hasExpectedDataGeneration()) {
-      // If a key does not exist, or if it exists but the updateID do not 
match, then fail this request.
-      if (dbKeyInfo == null) {
-        throw new OMException("Key not found during expected rewrite", 
OMException.ResultCodes.KEY_NOT_FOUND);
-      }
-      if (dbKeyInfo.getUpdateID() != keyArgs.getExpectedDataGeneration()) {
-        throw new OMException("Generation mismatch during expected rewrite", 
OMException.ResultCodes.KEY_NOT_FOUND);
+      long expectedGen = keyArgs.getExpectedDataGeneration();
+      // If expectedGen is EXPECTED_GEN_CREATE_IF_NOT_EXISTS, it means the key 
MUST NOT exist (If-None-Match)
+      if (expectedGen == OzoneConsts.EXPECTED_GEN_CREATE_IF_NOT_EXISTS) {
+        if (dbKeyInfo != null) {
+          throw new OMException("Key already exists",
+              OMException.ResultCodes.KEY_ALREADY_EXISTS);
+        }
+      } else {
+        // If a key does not exist, or if it exists but the updateID do not 
match, then fail this request.
+        if (dbKeyInfo == null) {
+          throw new OMException("Key not found during expected rewrite", 
OMException.ResultCodes.KEY_NOT_FOUND);
+        }
+        if (dbKeyInfo.getUpdateID() != expectedGen) {
+          throw new OMException("Generation mismatch during expected rewrite",
+              OMException.ResultCodes.KEY_NOT_FOUND);
+        }
       }
     }
   }
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyCommitRequest.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyCommitRequest.java
index 02d913160bc..652a7aa0fcf 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyCommitRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyCommitRequest.java
@@ -17,6 +17,7 @@
 
 package org.apache.hadoop.ozone.om.request.key;
 
+import static 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.KEY_ALREADY_EXISTS;
 import static 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.KEY_NOT_FOUND;
 import static 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.OK;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -279,6 +280,76 @@ public void testAtomicRewrite() throws Exception {
     assertEquals(acls, committedKey.getAcls());
   }
 
+  @Test
+  public void testAtomicCreateIfNotExistsCommitKeyAbsent() throws Exception {
+    Table<String, OmKeyInfo> openKeyTable = 
omMetadataManager.getOpenKeyTable(getBucketLayout());
+    Table<String, OmKeyInfo> closedKeyTable = 
omMetadataManager.getKeyTable(getBucketLayout());
+
+    OMRequest modifiedOmRequest = doPreExecute(createCommitKeyRequest());
+    OMKeyCommitRequest omKeyCommitRequest = 
getOmKeyCommitRequest(modifiedOmRequest);
+    KeyArgs keyArgs = modifiedOmRequest.getCommitKeyRequest().getKeyArgs();
+
+    OMRequestTestUtils.addVolumeAndBucketToDB(volumeName, bucketName,
+        omMetadataManager, omKeyCommitRequest.getBucketLayout());
+
+    List<OmKeyLocationInfo> allocatedLocationList =
+        keyArgs.getKeyLocationsList().stream()
+            .map(OmKeyLocationInfo::getFromProtobuf)
+            .collect(Collectors.toList());
+
+    OmKeyInfo.Builder omKeyInfoBuilder = OMRequestTestUtils.createOmKeyInfo(
+        volumeName, bucketName, keyName, replicationConfig,
+        new OmKeyLocationInfoGroup(version, new ArrayList<>()));
+    
omKeyInfoBuilder.setExpectedDataGeneration(OzoneConsts.EXPECTED_GEN_CREATE_IF_NOT_EXISTS);
+
+    String openKey = addKeyToOpenKeyTable(allocatedLocationList, 
omKeyInfoBuilder);
+    assertNotNull(openKeyTable.get(openKey));
+    assertNull(closedKeyTable.get(getOzonePathKey()));
+
+    OMClientResponse omClientResponse =
+        omKeyCommitRequest.validateAndUpdateCache(ozoneManager, 100L);
+    assertEquals(OK, omClientResponse.getOMResponse().getStatus());
+
+    OmKeyInfo committedKey = closedKeyTable.get(getOzonePathKey());
+    assertNotNull(committedKey);
+    assertNull(committedKey.getExpectedDataGeneration());
+  }
+
+  @Test
+  public void testAtomicCreateIfNotExistsCommitKeyAlreadyExists() throws 
Exception {
+    Table<String, OmKeyInfo> openKeyTable = 
omMetadataManager.getOpenKeyTable(getBucketLayout());
+    Table<String, OmKeyInfo> closedKeyTable = 
omMetadataManager.getKeyTable(getBucketLayout());
+
+    OMRequest modifiedOmRequest = doPreExecute(createCommitKeyRequest());
+    OMKeyCommitRequest omKeyCommitRequest = 
getOmKeyCommitRequest(modifiedOmRequest);
+    KeyArgs keyArgs = modifiedOmRequest.getCommitKeyRequest().getKeyArgs();
+
+    OMRequestTestUtils.addVolumeAndBucketToDB(volumeName, bucketName,
+        omMetadataManager, omKeyCommitRequest.getBucketLayout());
+
+    List<OmKeyLocationInfo> allocatedLocationList =
+        keyArgs.getKeyLocationsList().stream()
+            .map(OmKeyLocationInfo::getFromProtobuf)
+            .collect(Collectors.toList());
+
+    OmKeyInfo.Builder omKeyInfoBuilder = OMRequestTestUtils.createOmKeyInfo(
+        volumeName, bucketName, keyName, replicationConfig,
+        new OmKeyLocationInfoGroup(version, new ArrayList<>()));
+    
omKeyInfoBuilder.setExpectedDataGeneration(OzoneConsts.EXPECTED_GEN_CREATE_IF_NOT_EXISTS);
+
+    String openKey = addKeyToOpenKeyTable(allocatedLocationList, 
omKeyInfoBuilder);
+    assertNotNull(openKeyTable.get(openKey));
+
+    OmKeyInfo existingClosedKey = OMRequestTestUtils.createOmKeyInfo(
+        volumeName, bucketName, keyName, replicationConfig,
+        new OmKeyLocationInfoGroup(version, new ArrayList<>())).build();
+    closedKeyTable.put(getOzonePathKey(), existingClosedKey);
+
+    OMClientResponse omClientResponse =
+        omKeyCommitRequest.validateAndUpdateCache(ozoneManager, 100L);
+    assertEquals(KEY_ALREADY_EXISTS, 
omClientResponse.getOMResponse().getStatus());
+  }
+
   @Test
   public void testValidateAndUpdateCacheWithUncommittedBlocks()
       throws Exception {
@@ -456,7 +527,7 @@ private Map<String, RepeatedOmKeyInfo> doKeyCommit(boolean 
isHSync,
         .collect(Collectors.toList());
     String openKey = addKeyToOpenKeyTable(allocatedBlockList);
     String ozoneKey = getOzonePathKey();
-    
+
     OMClientResponse omClientResponse =
         omKeyCommitRequest.validateAndUpdateCache(ozoneManager, 100L);
     assertEquals(OK,
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyCreateRequest.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyCreateRequest.java
index 1666f4cb38e..52c9eeea07d 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyCreateRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyCreateRequest.java
@@ -27,6 +27,7 @@
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_ENABLE_FILESYSTEM_PATHS;
 import static 
org.apache.hadoop.ozone.om.request.OMRequestTestUtils.addVolumeAndBucketToDB;
 import static 
org.apache.hadoop.ozone.om.request.OMRequestTestUtils.createOmKeyInfo;
+import static 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.KEY_ALREADY_EXISTS;
 import static 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.KEY_NOT_FOUND;
 import static 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.NOT_A_FILE;
 import static 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status.OK;
@@ -146,6 +147,89 @@ public void preExecuteRejectsInvalidReplication() {
     assertEquals(OMException.ResultCodes.INVALID_REQUEST, e.getResult());
   }
 
+  @ParameterizedTest
+  @MethodSource("data")
+  public void testCreateKeyExpectedGenCreateIfNotExistsKeyMissing(
+      boolean setKeyPathLock, boolean setFileSystemPaths) throws Exception {
+    when(ozoneManager.getOzoneLockProvider()).thenReturn(
+        new OzoneLockProvider(setKeyPathLock, setFileSystemPaths));
+
+    OMRequest modifiedOmRequest = doPreExecute(createKeyRequest(
+        false, 0, 100L, replicationConfig,
+        OzoneConsts.EXPECTED_GEN_CREATE_IF_NOT_EXISTS));
+    OMKeyCreateRequest omKeyCreateRequest = 
getOMKeyCreateRequest(modifiedOmRequest);
+
+    addVolumeAndBucketToDB(volumeName, bucketName, omMetadataManager, 
getBucketLayout());
+
+    long id = modifiedOmRequest.getCreateKeyRequest().getClientID();
+    OMClientResponse response =
+        omKeyCreateRequest.validateAndUpdateCache(ozoneManager, 100L);
+
+    checkResponse(modifiedOmRequest, response, id, false, getBucketLayout());
+  }
+
+  @ParameterizedTest
+  @MethodSource("data")
+  public void testCreateKeyExpectedGenCreateIfNotExistsKeyAlreadyExists(
+      boolean setKeyPathLock, boolean setFileSystemPaths) throws Exception {
+    when(ozoneManager.getOzoneLockProvider()).thenReturn(
+        new OzoneLockProvider(setKeyPathLock, setFileSystemPaths));
+
+    OMRequest modifiedOmRequest = doPreExecute(createKeyRequest(
+        false, 0, 100L, replicationConfig,
+        OzoneConsts.EXPECTED_GEN_CREATE_IF_NOT_EXISTS));
+    OMKeyCreateRequest omKeyCreateRequest = 
getOMKeyCreateRequest(modifiedOmRequest);
+
+    addVolumeAndBucketToDB(volumeName, bucketName, omMetadataManager, 
getBucketLayout());
+
+    OmKeyInfo existingKeyInfo = createOmKeyInfo(
+        volumeName, bucketName, keyName, 
replicationConfig).setUpdateID(1L).build();
+    omMetadataManager.getKeyTable(getBucketLayout()).put(getOzoneKey(), 
existingKeyInfo);
+
+    long id = modifiedOmRequest.getCreateKeyRequest().getClientID();
+    String openKey = getOpenKey(id);
+
+    OMClientResponse response =
+        omKeyCreateRequest.validateAndUpdateCache(ozoneManager, 100L);
+    assertEquals(KEY_ALREADY_EXISTS, response.getOMResponse().getStatus());
+
+    // As we got error, no entry should be created in openKeyTable.
+    OmKeyInfo openKeyInfo =
+        omMetadataManager.getOpenKeyTable(getBucketLayout()).get(openKey);
+    assertNull(openKeyInfo);
+  }
+
+  @ParameterizedTest
+  @MethodSource("data")
+  public void testCreateKeyExpectedGenMismatchReturnsKeyGenerationMismatch(
+      boolean setKeyPathLock, boolean setFileSystemPaths) throws Exception {
+    when(ozoneManager.getOzoneLockProvider()).thenReturn(
+        new OzoneLockProvider(setKeyPathLock, setFileSystemPaths));
+
+    long expectedGen = 1L;
+    OMRequest modifiedOmRequest = doPreExecute(createKeyRequest(
+        false, 0, 100L, replicationConfig, expectedGen));
+    OMKeyCreateRequest omKeyCreateRequest = 
getOMKeyCreateRequest(modifiedOmRequest);
+
+    addVolumeAndBucketToDB(volumeName, bucketName, omMetadataManager, 
getBucketLayout());
+
+    OmKeyInfo existingKeyInfo = createOmKeyInfo(
+        volumeName, bucketName, keyName, 
replicationConfig).setUpdateID(2L).build();
+    omMetadataManager.getKeyTable(getBucketLayout()).put(getOzoneKey(), 
existingKeyInfo);
+
+    long id = modifiedOmRequest.getCreateKeyRequest().getClientID();
+    String openKey = getOpenKey(id);
+
+    OMClientResponse response =
+        omKeyCreateRequest.validateAndUpdateCache(ozoneManager, 100L);
+    assertEquals(KEY_NOT_FOUND, response.getOMResponse().getStatus());
+
+    // As we got error, no entry should be created in openKeyTable.
+    OmKeyInfo openKeyInfo =
+        omMetadataManager.getOpenKeyTable(getBucketLayout()).get(openKey);
+    assertNull(openKeyInfo);
+  }
+
   @ParameterizedTest
   @MethodSource("data")
   public void testValidateAndUpdateCache(


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

Reply via email to