peterxcli commented on code in PR #11140:
URL: https://github.com/apache/ozone/pull/11140#discussion_r3879637654


##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyCopyRequest.java:
##########
@@ -0,0 +1,326 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.ozone.om.request.key;
+
+import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.KEY_ALREADY_EXISTS;
+import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.KEY_NOT_FOUND;
+import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.NOT_SUPPORTED_OPERATION;
+import static 
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.LeveledResource.BUCKET_LOCK;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
+import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.audit.OMAction;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext;
+import org.apache.hadoop.ozone.om.helpers.BucketLayout;
+import org.apache.hadoop.ozone.om.helpers.KeyValueUtil;
+import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
+import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
+import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfoGroup;
+import org.apache.hadoop.ozone.om.request.OMClientRequestUtils;
+import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.key.OMKeyCopyResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CopyKeyRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CopyKeyResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyArgs;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
+import org.apache.hadoop.util.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Handles a server side key copy: the destination key is created as an
+ * independent key that reuses the source key's committed block locations, so 
no
+ * data is read or written. Both keys are tagged with a shared block group id
+ * and the group's sharer count is tracked in the sharedBlockGroupTable, which
+ * {@link org.apache.hadoop.ozone.om.service.KeyDeletingService} consults so 
the
+ * blocks are only released once the last sharer is reclaimed.
+ *
+ * <p>This is the proof-of-concept scope. A copy is rejected, and the caller is
+ * expected to fall back to reading and rewriting the data, when it would need
+ * to cross a bucket, overwrite an existing key, or touch encrypted, GDPR or
+ * hsync-active keys.
+ */
+public class OMKeyCopyRequest extends OMKeyRequest {
+
+  private static final Logger LOG =
+      LoggerFactory.getLogger(OMKeyCopyRequest.class);
+
+  public OMKeyCopyRequest(OMRequest omRequest, BucketLayout bucketLayout) {
+    super(omRequest, bucketLayout);
+  }
+
+  @Override
+  public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+    CopyKeyRequest copyKeyRequest =
+        super.preExecute(ozoneManager).getCopyKeyRequest();
+    Objects.requireNonNull(copyKeyRequest, "copyKeyRequest == null");
+
+    KeyArgs sourceKeyArgs = copyKeyRequest.getSourceKeyArgs();
+    KeyArgs destinationKeyArgs = copyKeyRequest.getDestinationKeyArgs();
+
+    if 
(!sourceKeyArgs.getVolumeName().equals(destinationKeyArgs.getVolumeName())
+        || 
!sourceKeyArgs.getBucketName().equals(destinationKeyArgs.getBucketName())) {
+      throw new OMException("Server side copy across buckets is not supported 
yet",
+          NOT_SUPPORTED_OPERATION);
+    }
+    if (sourceKeyArgs.getKeyName().equals(destinationKeyArgs.getKeyName())) {
+      throw new OMException("Server side copy onto the source key is not 
supported",
+          NOT_SUPPORTED_OPERATION);
+    }
+
+    KeyArgs.Builder normalizedSource = sourceKeyArgs.toBuilder()
+        
.setKeyName(validateAndNormalizeKey(ozoneManager.getEnableFileSystemPaths(),
+            sourceKeyArgs.getKeyName(), getBucketLayout()));
+    KeyArgs.Builder normalizedDestination = destinationKeyArgs.toBuilder()
+        
.setKeyName(validateAndNormalizeKey(ozoneManager.getEnableFileSystemPaths(),
+            destinationKeyArgs.getKeyName(), getBucketLayout()))
+        .setModificationTime(Time.now());
+
+    KeyArgs resolvedSource = 
resolveBucketAndCheckKeyAcls(normalizedSource.build(),
+        ozoneManager, ACLType.READ);
+    KeyArgs resolvedDestination = 
resolveBucketAndCheckKeyAcls(normalizedDestination.build(),
+        ozoneManager, ACLType.CREATE);
+
+    return getOmRequest().toBuilder()
+        .setUserInfo(getUserInfo())
+        .setCopyKeyRequest(copyKeyRequest.toBuilder()
+            .setSourceKeyArgs(resolvedSource)
+            .setDestinationKeyArgs(resolvedDestination))
+        .build();
+  }
+
+  @Override
+  @SuppressWarnings("checkstyle:methodlength")
+  public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, 
ExecutionContext context) {
+    final long trxnLogIndex = context.getIndex();

Review Comment:
   Agreed, and fixed in b2cc9a40c0.
   
   Added a `SERVER_SIDE_COPY` layout feature and gated 
`OMKeyCopyRequest.preExecute` with 
`@DisallowedUntilLayoutVersion(SERVER_SIDE_COPY)`. Since a copy is the only 
thing that can create a shared block, gating the copy means no shared block can 
exist until every OM in the cluster understands the tag, which closes the 
mixed-version window.
   
   On the second half of the suggestion, disabling shared-block behaviour in 
the deletion and purge paths until finalization: I would rather not. Those 
checks have to be unconditional. Pre-finalization they are inert anyway because 
no tagged key exists, and post-finalization they are exactly what keeps a 
shared block alive while another key still references it, so making them 
conditional would reintroduce the failure this change exists to prevent. 
Downgrade past a finalized layout version is already disallowed.
   
   Worth noting for anyone reading the diff: the client side still has no 
`OzoneManagerVersion` negotiation, so against an OM that has not finalized, a 
copy fails rather than transparently falling back to a read-and-rewrite copy. 
That is listed in the PR description as outstanding.



-- 
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]

Reply via email to