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

SaketaChalamchala 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 87d2bd8577b HDDS-15387. Selectively deserialize KeyInfo/DirectoryInfo 
fields for snapshot diff (#10603)
87d2bd8577b is described below

commit 87d2bd8577b3d30843e8e2b600acc18944cd3d0e
Author: SaketaChalamchala <[email protected]>
AuthorDate: Mon Jul 13 14:52:17 2026 -0700

    HDDS-15387. Selectively deserialize KeyInfo/DirectoryInfo fields for 
snapshot diff (#10603)
    
    Co-authored-by: Wei-Chiu Chuang <[email protected]>
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
---
 .../src/main/proto/OmClientProtocol.proto          |   4 +
 .../ozone/om/snapshot/SnapshotDiffValueParser.java | 359 +++++++++++++++++++++
 .../om/snapshot/TestSnapshotDiffValueParser.java   | 249 ++++++++++++++
 3 files changed, 612 insertions(+)

diff --git 
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto 
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index f50d812e51f..bb8f54c79c5 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -1187,6 +1187,8 @@ message FileChecksumProto {
 }
 
 message KeyInfo {
+    // When adding a new field, update 
SnapshotDiffValueParser.computeKeyInfoCompareSignature()
+    // if it is required for snapshot diff comparisons.
     required string volumeName = 1;
     required string bucketName = 2;
     required string keyName = 3;
@@ -1251,6 +1253,8 @@ message BasicKeyInfo {
 }
 
 message DirectoryInfo {
+    // When adding a new field, update 
SnapshotDiffValueParser.computeDirectoryInfoCompareSignature()
+    // if it is required for snapshot diff comparisons.
     required string name = 1;
     required uint64 creationTime = 2;
     required uint64 modificationTime = 3;
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffValueParser.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffValueParser.java
new file mode 100644
index 00000000000..b1c09cd082a
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffValueParser.java
@@ -0,0 +1,359 @@
+/*
+ * 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.snapshot;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.WireFormat;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.KeyValue;
+import org.apache.hadoop.ozone.OzoneConsts;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DirectoryInfo;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyInfo;
+
+/**
+ * Parses snapshot diff values without full deserialization.
+ */
+public final class SnapshotDiffValueParser {
+  private static final int INT_BYTES = 4;
+  private static final int LONG_BYTES = 8;
+  private static final int HSYNC_METADATA_PRESENT_TAG = 1001;
+  private static final String DIGEST_ALGORITHM = "SHA-256";
+
+  private SnapshotDiffValueParser() {
+  }
+
+  public static ParsedRequiredInfo parseKeyInfoRequiredFields(byte[] value, 
boolean includeUpdateId)
+      throws IOException {
+    CodedInputStream input = CodedInputStream.newInstance(value);
+    long updateId = 0L;
+    long objectId = 0L;
+    long parentId = 0L;
+    boolean hasUpdateId = false;
+    String keyName = null;
+
+    int tag;
+    while ((tag = input.readTag()) != 0) {
+      int fieldNumber = WireFormat.getTagFieldNumber(tag);
+      switch (fieldNumber) {
+      case KeyInfo.KEYNAME_FIELD_NUMBER:
+        keyName = input.readString();
+        break;
+      case KeyInfo.OBJECTID_FIELD_NUMBER:
+        objectId = input.readUInt64();
+        break;
+      case KeyInfo.UPDATEID_FIELD_NUMBER:
+        if (includeUpdateId) {
+          updateId = input.readUInt64();
+          hasUpdateId = true;
+        } else {
+          input.skipField(tag);
+        }
+        break;
+      case KeyInfo.PARENTID_FIELD_NUMBER:
+        parentId = input.readUInt64();
+        break;
+      default:
+        input.skipField(tag);
+        break;
+      }
+    }
+
+    return new ParsedRequiredInfo(updateId, hasUpdateId, objectId, parentId, 
keyName);
+  }
+
+  public static byte[] computeKeyInfoCompareSignature(byte[] value) throws 
IOException {
+    CodedInputStream input = CodedInputStream.newInstance(value);
+    MessageDigest digest = newDigest();
+    AtomicBoolean hasHsyncMetadata = new AtomicBoolean(false);
+    int keyLocationListCount = 0;
+    ByteString latestKeyLocationList = null;
+    List<byte[]> metadataSignatures = new ArrayList<>();
+    List<byte[]> tagSignatures = new ArrayList<>();
+
+    int tag;
+    while ((tag = input.readTag()) != 0) {
+      int fieldNumber = WireFormat.getTagFieldNumber(tag);
+      switch (fieldNumber) {
+      case KeyInfo.DATASIZE_FIELD_NUMBER:
+        updateDigestWithLong(digest, fieldNumber, input.readUInt64());
+        break;
+      case KeyInfo.KEYLOCATIONLIST_FIELD_NUMBER:
+        latestKeyLocationList = input.readBytes();
+        keyLocationListCount++;
+        break;
+      case KeyInfo.METADATA_FIELD_NUMBER:
+        byte[] metadataDigest = 
parseKeyValueDigest(input.readBytes().toByteArray(), true, hasHsyncMetadata);
+        if (metadataDigest != null) {
+          metadataSignatures.add(metadataDigest);
+        }
+        break;
+      case KeyInfo.FILECHECKSUM_FIELD_NUMBER:
+      case KeyInfo.ACLS_FIELD_NUMBER:
+        updateDigestWithBytes(digest, fieldNumber, input.readBytes());
+        break;
+      case KeyInfo.TAGS_FIELD_NUMBER:
+        byte[] tagDigest = 
parseKeyValueDigest(input.readBytes().toByteArray(), false, null);
+        if (tagDigest != null) {
+          tagSignatures.add(tagDigest);
+        }
+        break;
+      default:
+        input.skipField(tag);
+        break;
+      }
+    }
+
+    if (latestKeyLocationList != null) {
+      updateDigestWithBytes(digest, KeyInfo.KEYLOCATIONLIST_FIELD_NUMBER, 
latestKeyLocationList);
+    }
+    addCanonicalizedDigest(digest, KeyInfo.METADATA_FIELD_NUMBER, 
metadataSignatures);
+    addCanonicalizedDigest(digest, KeyInfo.TAGS_FIELD_NUMBER, tagSignatures);
+    updateDigestWithBoolean(digest, HSYNC_METADATA_PRESENT_TAG, 
hasHsyncMetadata.get());
+    updateDigestWithInt(digest, keyLocationListCount);
+
+    return digest.digest();
+  }
+
+  public static ParsedRequiredInfo parseDirectoryInfoRequiredFields(byte[] 
value, boolean includeUpdateId)
+      throws IOException {
+    CodedInputStream input = CodedInputStream.newInstance(value);
+    long updateId = 0L;
+    long objectId = 0L;
+    long parentId = 0L;
+    boolean hasUpdateId = false;
+    String name = null;
+
+    int tag;
+    while ((tag = input.readTag()) != 0) {
+      int fieldNumber = WireFormat.getTagFieldNumber(tag);
+      switch (fieldNumber) {
+      case DirectoryInfo.NAME_FIELD_NUMBER:
+        name = input.readString();
+        break;
+      case DirectoryInfo.OBJECTID_FIELD_NUMBER:
+        objectId = input.readUInt64();
+        break;
+      case DirectoryInfo.UPDATEID_FIELD_NUMBER:
+        if (includeUpdateId) {
+          updateId = input.readUInt64();
+          hasUpdateId = true;
+        } else {
+          input.skipField(tag);
+        }
+        break;
+      case DirectoryInfo.PARENTID_FIELD_NUMBER:
+        parentId = input.readUInt64();
+        break;
+      default:
+        input.skipField(tag);
+        break;
+      }
+    }
+
+    return new ParsedRequiredInfo(updateId, hasUpdateId, objectId, parentId, 
name);
+  }
+
+  public static byte[] computeDirectoryInfoCompareSignature(byte[] value) 
throws IOException {
+    CodedInputStream input = CodedInputStream.newInstance(value);
+    MessageDigest digest = newDigest();
+    List<byte[]> metadataSignatures = new ArrayList<>();
+
+    int tag;
+    while ((tag = input.readTag()) != 0) {
+      int fieldNumber = WireFormat.getTagFieldNumber(tag);
+      switch (fieldNumber) {
+      case DirectoryInfo.METADATA_FIELD_NUMBER:
+        byte[] metadataDigest = 
parseKeyValueDigest(input.readBytes().toByteArray(), false, null);
+        if (metadataDigest != null) {
+          metadataSignatures.add(metadataDigest);
+        }
+        break;
+      case DirectoryInfo.ACLS_FIELD_NUMBER:
+        updateDigestWithBytes(digest, fieldNumber, input.readBytes());
+        break;
+      default:
+        input.skipField(tag);
+        break;
+      }
+    }
+
+    addCanonicalizedDigest(digest, DirectoryInfo.METADATA_FIELD_NUMBER, 
metadataSignatures);
+
+    return digest.digest();
+  }
+
+  private static void updateDigestWithLong(MessageDigest digest, int 
fieldNumber, long value) {
+    updateDigestWithInt(digest, fieldNumber);
+    byte[] buffer = new byte[LONG_BYTES];
+    for (int i = LONG_BYTES - 1; i >= 0; i--) {
+      buffer[i] = (byte) (value & 0xFFL);
+      value >>>= 8;
+    }
+    digest.update(buffer);
+  }
+
+  private static void updateDigestWithBytes(MessageDigest digest, int 
fieldNumber, ByteString value) {
+    updateDigestWithInt(digest, fieldNumber);
+    updateDigestWithInt(digest, value.size());
+    digest.update(value.toByteArray());
+  }
+
+  private static void updateTaggedString(MessageDigest digest, int 
fieldNumber, String value) {
+    updateDigestWithInt(digest, fieldNumber);
+    if (value == null) {
+      updateDigestWithInt(digest, 0);
+      return;
+    }
+    byte[] bytes = value.getBytes(java.nio.charset.StandardCharsets.UTF_8);
+    updateDigestWithInt(digest, bytes.length);
+    digest.update(bytes);
+  }
+
+  private static void updateDigestWithBoolean(MessageDigest digest, int 
fieldNumber, boolean value) {
+    updateDigestWithInt(digest, fieldNumber);
+    updateDigestWithInt(digest, value ? 1 : 0);
+  }
+
+  private static void updateDigestWithRawBytes(MessageDigest digest, int 
fieldNumber, byte[] value) {
+    updateDigestWithInt(digest, fieldNumber);
+    updateDigestWithInt(digest, value.length);
+    digest.update(value);
+  }
+
+  private static void updateDigestWithInt(MessageDigest digest, int value) {
+    byte[] buffer = new byte[INT_BYTES];
+    for (int i = INT_BYTES - 1; i >= 0; i--) {
+      buffer[i] = (byte) (value & 0xFF);
+      value >>>= 8;
+    }
+    digest.update(buffer);
+  }
+
+  private static MessageDigest newDigest() {
+    try {
+      return MessageDigest.getInstance(DIGEST_ALGORITHM);
+    } catch (NoSuchAlgorithmException e) {
+      throw new IllegalStateException("SHA-256 is not available", e);
+    }
+  }
+
+  private static void addCanonicalizedDigest(
+      MessageDigest digest, int fieldNumber, List<byte[]> metadataEntries) {
+    if (metadataEntries.isEmpty()) {
+      return;
+    }
+    metadataEntries.sort(SnapshotDiffValueParser::compareBytes);
+    MessageDigest metadataDigest = newDigest();
+    for (byte[] metadataEntry : metadataEntries) {
+      metadataDigest.update(metadataEntry);
+    }
+    updateDigestWithRawBytes(digest, fieldNumber, metadataDigest.digest());
+  }
+
+  private static byte[] parseKeyValueDigest(byte[] keyValueBytes, boolean 
computeHsync, AtomicBoolean hasHsync)
+      throws IOException {
+    if (keyValueBytes == null || keyValueBytes.length == 0) {
+      return null;
+    }
+    CodedInputStream input = CodedInputStream.newInstance(keyValueBytes);
+    String key = null;
+    String value = null;
+    while (!input.isAtEnd()) {
+      int tag = input.readTag();
+      if (tag == 0) {
+        break;
+      }
+      int fieldNumber = WireFormat.getTagFieldNumber(tag);
+      switch (fieldNumber) {
+      case KeyValue.KEY_FIELD_NUMBER:
+        key = input.readString();
+        break;
+      case KeyValue.VALUE_FIELD_NUMBER:
+        value = input.readString();
+        break;
+      default:
+        input.skipField(tag);
+        break;
+      }
+    }
+    if (computeHsync && hasHsync != null && 
OzoneConsts.HSYNC_CLIENT_ID.equals(key)) {
+      hasHsync.set(true);
+    }
+    MessageDigest entryDigest = newDigest();
+    updateTaggedString(entryDigest, KeyValue.KEY_FIELD_NUMBER, key);
+    updateTaggedString(entryDigest, KeyValue.VALUE_FIELD_NUMBER, value);
+    return entryDigest.digest();
+  }
+
+  private static int compareBytes(byte[] left, byte[] right) {
+    int length = Math.min(left.length, right.length);
+    for (int i = 0; i < length; i++) {
+      int diff = (left[i] & 0xFF) - (right[i] & 0xFF);
+      if (diff != 0) {
+        return diff;
+      }
+    }
+    return left.length - right.length;
+  }
+
+  /**
+   * Parsed fields shared by key and directory entries.
+   * Holds IDs and name with optional updateID when requested.
+   */
+  public static final class ParsedRequiredInfo {
+    private final long updateId;
+    private final boolean hasUpdateId;
+    private final long objectId;
+    private final long parentId;
+    private final String name;
+
+    private ParsedRequiredInfo(long updateId, boolean hasUpdateId, long 
objectId, long parentId, String name) {
+      this.updateId = updateId;
+      this.hasUpdateId = hasUpdateId;
+      this.objectId = objectId;
+      this.parentId = parentId;
+      this.name = name;
+    }
+
+    public long getUpdateId() {
+      return updateId;
+    }
+
+    public boolean hasUpdateId() {
+      return hasUpdateId;
+    }
+
+    public long getObjectId() {
+      return objectId;
+    }
+
+    public long getParentId() {
+      return parentId;
+    }
+
+    public String getName() {
+      return name;
+    }
+  }
+}
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffValueParser.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffValueParser.java
new file mode 100644
index 00000000000..261a97a130d
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffValueParser.java
@@ -0,0 +1,249 @@
+/*
+ * 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.snapshot;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.hadoop.fs.FileChecksum;
+import org.apache.hadoop.fs.MD5MD5CRC32GzipFileChecksum;
+import org.apache.hadoop.hdds.client.BlockID;
+import org.apache.hadoop.hdds.client.RatisReplicationConfig;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationFactor;
+import org.apache.hadoop.io.MD5Hash;
+import org.apache.hadoop.ozone.OzoneAcl;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.om.helpers.OmDirectoryInfo;
+import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
+import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfo;
+import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfoGroup;
+import org.junit.jupiter.api.Test;
+
+class TestSnapshotDiffValueParser {
+  private static final String VOLUME = "volume";
+  private static final String BUCKET = "bucket";
+  private static final String KEY_NAME = "dir/file";
+  private static final String DIR_NAME = "dir";
+  private static final long OBJECT_ID = 10L;
+  private static final long PARENT_ID = 20L;
+  private static final long UPDATE_ID = 30L;
+
+  @Test
+  void testKeyInfoParserSignatureAndUpdateId() throws Exception {
+    OmKeyInfo keyInfo = createKeyInfo(100L, 200L, 1024L, createChecksum((byte) 
1),
+        createMetadata("meta", "one"), createTags("tag", "one"), createAcls(),
+        Collections.singletonList(createKeyLocationGroup(1L)));
+    byte[] rawData = OmKeyInfo.getCodec().toPersistedFormat(keyInfo);
+
+    SnapshotDiffValueParser.ParsedRequiredInfo parsed =
+        SnapshotDiffValueParser.parseKeyInfoRequiredFields(rawData, true);
+    assertEquals(UPDATE_ID, parsed.getUpdateId());
+    assertEquals(OBJECT_ID, parsed.getObjectId());
+    assertEquals(PARENT_ID, parsed.getParentId());
+    assertEquals(KEY_NAME, parsed.getName());
+    assertFalse(SnapshotDiffValueParser.parseKeyInfoRequiredFields(rawData, 
false).hasUpdateId());
+
+    OmKeyInfo metadataChanged = createKeyInfo(100L, 200L, 1024L, 
createChecksum((byte) 1),
+        createMetadata("meta", "two"), createTags("tag", "one"), createAcls(),
+        Collections.singletonList(createKeyLocationGroup(1L)));
+    byte[] metadataRaw = 
OmKeyInfo.getCodec().toPersistedFormat(metadataChanged);
+    
assertFalse(Arrays.equals(SnapshotDiffValueParser.computeKeyInfoCompareSignature(rawData),
+        SnapshotDiffValueParser.computeKeyInfoCompareSignature(metadataRaw)));
+
+    Map<String, String> hsyncMetadata = createMetadata("meta", "one");
+    hsyncMetadata.put(OzoneConsts.HSYNC_CLIENT_ID, "client1");
+    OmKeyInfo hsyncChanged = createKeyInfo(100L, 200L, 1024L, 
createChecksum((byte) 1),
+        hsyncMetadata, createTags("tag", "one"), createAcls(),
+        Collections.singletonList(createKeyLocationGroup(1L)));
+    byte[] hsyncRaw = OmKeyInfo.getCodec().toPersistedFormat(hsyncChanged);
+    
assertFalse(Arrays.equals(SnapshotDiffValueParser.computeKeyInfoCompareSignature(rawData),
+        SnapshotDiffValueParser.computeKeyInfoCompareSignature(hsyncRaw)));
+
+    OmKeyInfo tagsChanged = createKeyInfo(100L, 200L, 1024L, 
createChecksum((byte) 1),
+        createMetadata("meta", "one"), createTags("tag", "two"), createAcls(),
+        Collections.singletonList(createKeyLocationGroup(1L)));
+    byte[] tagsRaw = OmKeyInfo.getCodec().toPersistedFormat(tagsChanged);
+    
assertFalse(Arrays.equals(SnapshotDiffValueParser.computeKeyInfoCompareSignature(rawData),
+        SnapshotDiffValueParser.computeKeyInfoCompareSignature(tagsRaw)));
+
+    OmKeyInfo aclsChanged = createKeyInfo(100L, 200L, 1024L, 
createChecksum((byte) 1),
+        createMetadata("meta", "one"), createTags("tag", "one"), 
createAcls("user:other:rw"),
+        Collections.singletonList(createKeyLocationGroup(1L)));
+    byte[] aclsRaw = OmKeyInfo.getCodec().toPersistedFormat(aclsChanged);
+    
assertFalse(Arrays.equals(SnapshotDiffValueParser.computeKeyInfoCompareSignature(rawData),
+        SnapshotDiffValueParser.computeKeyInfoCompareSignature(aclsRaw)));
+
+    OmKeyInfo checksumChanged = createKeyInfo(100L, 200L, 1024L, 
createChecksum((byte) 2),
+        createMetadata("meta", "one"), createTags("tag", "one"), createAcls(),
+        Collections.singletonList(createKeyLocationGroup(1L)));
+    byte[] checksumRaw = 
OmKeyInfo.getCodec().toPersistedFormat(checksumChanged);
+    
assertFalse(Arrays.equals(SnapshotDiffValueParser.computeKeyInfoCompareSignature(rawData),
+        SnapshotDiffValueParser.computeKeyInfoCompareSignature(checksumRaw)));
+
+    OmKeyInfo dataSizeChanged = createKeyInfo(100L, 200L, 2048L, 
createChecksum((byte) 1),
+        createMetadata("meta", "one"), createTags("tag", "one"), createAcls(),
+        Collections.singletonList(createKeyLocationGroup(1L)));
+    byte[] dataSizeRaw = 
OmKeyInfo.getCodec().toPersistedFormat(dataSizeChanged);
+    
assertFalse(Arrays.equals(SnapshotDiffValueParser.computeKeyInfoCompareSignature(rawData),
+        SnapshotDiffValueParser.computeKeyInfoCompareSignature(dataSizeRaw)));
+
+    OmKeyInfo locationCountChanged = createKeyInfo(100L, 200L, 1024L, 
createChecksum((byte) 1),
+        createMetadata("meta", "one"), createTags("tag", "one"), createAcls(),
+        createKeyLocationGroups(1L, 2L));
+    byte[] locationCountRaw = 
OmKeyInfo.getCodec().toPersistedFormat(locationCountChanged);
+    
assertFalse(Arrays.equals(SnapshotDiffValueParser.computeKeyInfoCompareSignature(rawData),
+        
SnapshotDiffValueParser.computeKeyInfoCompareSignature(locationCountRaw)));
+
+    OmKeyInfo latestLocationChanged = createKeyInfo(100L, 200L, 1024L, 
createChecksum((byte) 1),
+        createMetadata("meta", "one"), createTags("tag", "one"), createAcls(),
+        createKeyLocationGroups(1L, 3L));
+    byte[] latestLocationRaw = 
OmKeyInfo.getCodec().toPersistedFormat(latestLocationChanged);
+    
assertFalse(Arrays.equals(SnapshotDiffValueParser.computeKeyInfoCompareSignature(locationCountRaw),
+        
SnapshotDiffValueParser.computeKeyInfoCompareSignature(latestLocationRaw)));
+  }
+
+  @Test
+  void testKeyInfoIgnoresVolatileTimes() throws Exception {
+    OmKeyInfo keyInfo = createKeyInfo(100L, 200L, 1024L, createChecksum((byte) 
1),
+        createMetadata("meta", "one"), createTags("tag", "one"), createAcls(),
+        Collections.singletonList(createKeyLocationGroup(1L)));
+    OmKeyInfo timeChanged = createKeyInfo(110L, 220L, 1024L, 
createChecksum((byte) 1),
+        createMetadata("meta", "one"), createTags("tag", "one"), createAcls(),
+        Collections.singletonList(createKeyLocationGroup(1L)));
+
+    byte[] rawData = OmKeyInfo.getCodec().toPersistedFormat(keyInfo);
+    byte[] rawTimeChanged = 
OmKeyInfo.getCodec().toPersistedFormat(timeChanged);
+
+    assertArrayEquals(
+        SnapshotDiffValueParser.computeKeyInfoCompareSignature(rawData),
+        
SnapshotDiffValueParser.computeKeyInfoCompareSignature(rawTimeChanged));
+  }
+
+  @Test
+  void testDirectoryInfoSignatureAndParsing() throws Exception {
+    OmDirectoryInfo dirInfo = createDirectoryInfo(100L, 200L, 
createMetadata("meta", "one"), createAcls());
+    byte[] rawData = OmDirectoryInfo.getCodec().toPersistedFormat(dirInfo);
+    SnapshotDiffValueParser.ParsedRequiredInfo parsed =
+        SnapshotDiffValueParser.parseDirectoryInfoRequiredFields(rawData, 
true);
+    assertEquals(UPDATE_ID, parsed.getUpdateId());
+    assertEquals(OBJECT_ID, parsed.getObjectId());
+    assertEquals(PARENT_ID, parsed.getParentId());
+    assertEquals(DIR_NAME, parsed.getName());
+    
assertFalse(SnapshotDiffValueParser.parseDirectoryInfoRequiredFields(rawData, 
false).hasUpdateId());
+
+    OmDirectoryInfo metadataChanged = createDirectoryInfo(100L, 200L, 
createMetadata("meta", "two"), createAcls());
+    byte[] rawMetadataChanged = 
OmDirectoryInfo.getCodec().toPersistedFormat(metadataChanged);
+    
assertFalse(Arrays.equals(SnapshotDiffValueParser.computeDirectoryInfoCompareSignature(rawData),
+        
SnapshotDiffValueParser.computeDirectoryInfoCompareSignature(rawMetadataChanged)));
+
+    OmDirectoryInfo timeChanged = createDirectoryInfo(110L, 220L, 
createMetadata("meta", "one"), createAcls());
+    byte[] rawTimeChanged = 
OmDirectoryInfo.getCodec().toPersistedFormat(timeChanged);
+    assertArrayEquals(
+        SnapshotDiffValueParser.computeDirectoryInfoCompareSignature(rawData),
+        
SnapshotDiffValueParser.computeDirectoryInfoCompareSignature(rawTimeChanged));
+  }
+
+  @SuppressWarnings("checkstyle:ParameterNumber")
+  private static OmKeyInfo createKeyInfo(long creationTime, long 
modificationTime, long dataSize, FileChecksum checksum,
+      Map<String, String> metadata, Map<String, String> tags, List<OzoneAcl> 
acls,
+      List<OmKeyLocationInfoGroup> keyLocationGroups) {
+    return new OmKeyInfo.Builder()
+        .setVolumeName(VOLUME)
+        .setBucketName(BUCKET)
+        .setKeyName(KEY_NAME)
+        .setCreationTime(creationTime)
+        .setModificationTime(modificationTime)
+        
.setReplicationConfig(RatisReplicationConfig.getInstance(ReplicationFactor.ONE))
+        .setObjectID(OBJECT_ID)
+        .setParentObjectID(PARENT_ID)
+        .setUpdateID(UPDATE_ID)
+        .setDataSize(dataSize)
+        .setOmKeyLocationInfos(keyLocationGroups)
+        .setFileChecksum(checksum)
+        .addAllMetadata(metadata)
+        .setTags(tags)
+        .setAcls(acls)
+        .build();
+  }
+
+  private static OmDirectoryInfo createDirectoryInfo(long creationTime, long 
modificationTime,
+      Map<String, String> metadata, List<OzoneAcl> acls) {
+    return OmDirectoryInfo.newBuilder()
+        .setName(DIR_NAME)
+        .setCreationTime(creationTime)
+        .setModificationTime(modificationTime)
+        .setObjectID(OBJECT_ID)
+        .setParentObjectID(PARENT_ID)
+        .setUpdateID(UPDATE_ID)
+        .addAllMetadata(metadata)
+        .setAcls(acls)
+        .build();
+  }
+
+  private static OmKeyLocationInfoGroup createKeyLocationGroup(long blockId) {
+    OmKeyLocationInfo location = new OmKeyLocationInfo.Builder()
+        .setBlockID(new BlockID(blockId, blockId))
+        .build();
+    return new OmKeyLocationInfoGroup(0, Collections.singletonList(location));
+  }
+
+  private static List<OmKeyLocationInfoGroup> createKeyLocationGroups(long... 
blockIds) {
+    List<OmKeyLocationInfoGroup> groups = new java.util.ArrayList<>();
+    int version = 0;
+    for (long blockId : blockIds) {
+      OmKeyLocationInfo location = new OmKeyLocationInfo.Builder()
+          .setBlockID(new BlockID(blockId, blockId))
+          .build();
+      groups.add(new OmKeyLocationInfoGroup(version++, 
Collections.singletonList(location)));
+    }
+    return groups;
+  }
+
+  private static FileChecksum createChecksum(byte value) {
+    byte[] bytes = new byte[32];
+    Arrays.fill(bytes, value);
+    MD5Hash fileMd5 = MD5Hash.digest(bytes);
+    return new MD5MD5CRC32GzipFileChecksum(0, 0, fileMd5);
+  }
+
+  private static Map<String, String> createMetadata(String key, String value) {
+    Map<String, String> metadata = new LinkedHashMap<>();
+    metadata.put(key, value);
+    return metadata;
+  }
+
+  private static Map<String, String> createTags(String key, String value) {
+    Map<String, String> tags = new LinkedHashMap<>();
+    tags.put(key, value);
+    return tags;
+  }
+
+  private static List<OzoneAcl> createAcls() {
+    return Collections.singletonList(OzoneAcl.parseAcl("user:test:rw"));
+  }
+
+  private static List<OzoneAcl> createAcls(String acl) {
+    return Collections.singletonList(OzoneAcl.parseAcl(acl));
+  }
+}


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

Reply via email to