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

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


The following commit(s) were added to refs/heads/master by this push:
     new 639f1bf0b RATIS-1910. Deduplicate RaftGroupId and ClientId. (#940)
639f1bf0b is described below

commit 639f1bf0bc1d4b8c72e22cfcd50f3f1d2b7df641
Author: Tsz-Wo Nicholas Sze <[email protected]>
AuthorDate: Wed Oct 18 09:59:52 2023 -0700

    RATIS-1910. Deduplicate RaftGroupId and ClientId. (#940)
---
 .../java/org/apache/ratis/protocol/ClientId.java   | 24 +++++----
 .../org/apache/ratis/protocol/RaftGroupId.java     | 23 ++++----
 .../java/org/apache/ratis/protocol/RaftId.java     | 61 +++++++++++++++++-----
 .../java/org/apache/ratis/protocol/RaftPeerId.java |  9 ++--
 .../com/google/protobuf/ByteStringUtils.java       | 30 +++++++++++
 .../java/org/apache/ratis/protocol/TestRaftId.java | 21 +++++++-
 6 files changed, 129 insertions(+), 39 deletions(-)

diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/ClientId.java 
b/ratis-common/src/main/java/org/apache/ratis/protocol/ClientId.java
index 8c3e4c714..82ae426d5 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/ClientId.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/ClientId.java
@@ -19,34 +19,38 @@ package org.apache.ratis.protocol;
 
 import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
 
-import java.util.Optional;
 import java.util.UUID;
 
 /**
- * Id of Raft client. Should be globally unique so that raft peers can use it
+ * The id of RaftClient. Should be globally unique so that raft peers can use 
it
  * to correctly identify retry requests from the same client.
  */
 public final class ClientId extends RaftId {
-  private static final ClientId EMPTY_CLIENT_ID = new 
ClientId(ZERO_UUID_BYTESTRING);
+  private static final Factory<ClientId> FACTORY = new Factory<ClientId>() {
+    @Override
+    ClientId newInstance(UUID uuid, ByteString bytes) {
+      return bytes == null? new ClientId(uuid): new ClientId(uuid, bytes);
+    }
+  };
 
   public static ClientId emptyClientId() {
-    return EMPTY_CLIENT_ID;
+    return FACTORY.emptyId();
   }
 
   public static ClientId randomId() {
-    return new ClientId(UUID.randomUUID());
+    return FACTORY.randomId();
   }
 
-  public static ClientId valueOf(ByteString data) {
-    return Optional.ofNullable(data).filter(d -> 
!d.isEmpty()).map(ClientId::new).orElse(EMPTY_CLIENT_ID);
+  public static ClientId valueOf(ByteString bytes) {
+    return FACTORY.valueOf(bytes);
   }
 
   public static ClientId valueOf(UUID uuid) {
-    return new ClientId(uuid);
+    return FACTORY.valueOf(uuid);
   }
 
-  private ClientId(ByteString data) {
-    super(data);
+  private ClientId(UUID uuid, ByteString bytes) {
+    super(uuid, bytes);
   }
 
   private ClientId(UUID uuid) {
diff --git 
a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroupId.java 
b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroupId.java
index f4820d614..968b9e500 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroupId.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftGroupId.java
@@ -23,34 +23,39 @@ import java.util.UUID;
 
 /**
  * The id of a raft group.
- *
+ * <p>
  * This is a value-based class.
  */
 public final class RaftGroupId extends RaftId {
-  private static final RaftGroupId EMPTY_GROUP_ID = new 
RaftGroupId(ZERO_UUID_BYTESTRING);
+  private static final Factory<RaftGroupId> FACTORY = new 
Factory<RaftGroupId>() {
+    @Override
+    RaftGroupId newInstance(UUID uuid, ByteString bytes) {
+      return bytes == null? new RaftGroupId(uuid) : new RaftGroupId(uuid, 
bytes);
+    }
+  };
 
   public static RaftGroupId emptyGroupId() {
-    return EMPTY_GROUP_ID;
+    return FACTORY.emptyId();
   }
 
   public static RaftGroupId randomId() {
-    return new RaftGroupId(UUID.randomUUID());
+    return FACTORY.randomId();
   }
 
   public static RaftGroupId valueOf(UUID uuid) {
-    return new RaftGroupId(uuid);
+    return FACTORY.valueOf(uuid);
   }
 
-  public static RaftGroupId valueOf(ByteString data) {
-    return new RaftGroupId(data);
+  public static RaftGroupId valueOf(ByteString bytes) {
+    return FACTORY.valueOf(bytes);
   }
 
   private RaftGroupId(UUID id) {
     super(id);
   }
 
-  private RaftGroupId(ByteString data) {
-    super(data);
+  private RaftGroupId(UUID uuid, ByteString bytes) {
+    super(uuid, bytes);
   }
 
   @Override
diff --git a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftId.java 
b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftId.java
index b0f6228b2..f1d07a980 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftId.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftId.java
@@ -17,13 +17,17 @@
  */
 package org.apache.ratis.protocol;
 
+import org.apache.ratis.thirdparty.com.google.common.cache.Cache;
+import org.apache.ratis.thirdparty.com.google.common.cache.CacheBuilder;
 import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
+import org.apache.ratis.thirdparty.com.google.protobuf.ByteStringUtils;
 import org.apache.ratis.util.JavaUtils;
 import org.apache.ratis.util.Preconditions;
 
 import java.nio.ByteBuffer;
 import java.util.Objects;
 import java.util.UUID;
+import java.util.concurrent.ExecutionException;
 import java.util.function.Supplier;
 
 /** Unique identifier implemented using {@link UUID}. */
@@ -32,24 +36,52 @@ public abstract class RaftId {
   static final ByteString ZERO_UUID_BYTESTRING = toByteString(ZERO_UUID);
   private static final int BYTE_LENGTH = 16;
 
-  private static void checkLength(int length, String name) {
-    Preconditions.assertTrue(length == BYTE_LENGTH,
-        "%s = %s != BYTE_LENGTH = %s", name, length, BYTE_LENGTH);
-  }
-
-  private static UUID toUuid(ByteString bytes) {
+  static UUID toUuid(ByteString bytes) {
     Objects.requireNonNull(bytes, "bytes == null");
-    checkLength(bytes.size(), "bytes.size()");
+    Preconditions.assertSame(BYTE_LENGTH, bytes.size(), "bytes.size()");
     final ByteBuffer buf = bytes.asReadOnlyByteBuffer();
     return new UUID(buf.getLong(), buf.getLong());
   }
 
-  private static ByteString toByteString(UUID uuid) {
+  static ByteString toByteString(UUID uuid) {
     Objects.requireNonNull(uuid, "uuid == null");
-    final ByteBuffer buf = ByteBuffer.wrap(new byte[BYTE_LENGTH]);
-    buf.putLong(uuid.getMostSignificantBits());
-    buf.putLong(uuid.getLeastSignificantBits());
-    return ByteString.copyFrom(buf.array());
+    final byte[] array = new byte[BYTE_LENGTH];
+    ByteBuffer.wrap(array)
+        .putLong(uuid.getMostSignificantBits())
+        .putLong(uuid.getLeastSignificantBits());
+    return ByteStringUtils.unsafeWrap(array);
+  }
+
+  abstract static class Factory<ID extends RaftId> {
+    private final Cache<UUID, ID> cache = CacheBuilder.newBuilder()
+        .weakValues()
+        .build();
+
+    abstract ID newInstance(UUID uuid, ByteString bytes);
+
+    private ID valueOf(UUID uuid, ByteString bytes) {
+      try {
+        return cache.get(uuid, () -> newInstance(uuid, bytes));
+      } catch (ExecutionException e) {
+        throw new IllegalStateException("Failed to valueOf(" + uuid + ")", e);
+      }
+    }
+
+    final ID valueOf(UUID uuid) {
+      return valueOf(uuid, null);
+    }
+
+    final ID valueOf(ByteString bytes) {
+      return bytes != null? valueOf(toUuid(bytes), bytes): emptyId();
+    }
+
+    ID emptyId() {
+      return valueOf(ZERO_UUID, ZERO_UUID_BYTESTRING);
+    }
+
+    ID randomId() {
+      return valueOf(UUID.randomUUID(), null);
+    }
   }
 
   private final UUID uuid;
@@ -68,8 +100,9 @@ public abstract class RaftId {
         () -> "Failed to create " + JavaUtils.getClassSimpleName(getClass()) + 
": UUID " + ZERO_UUID + " is reserved.");
   }
 
-  RaftId(ByteString uuidBytes) {
-    this(toUuid(uuidBytes), () -> uuidBytes);
+  RaftId(UUID uuid, ByteString bytes) {
+    this(uuid, () -> bytes);
+    Preconditions.assertTrue(toUuid(bytes).equals(uuid));
   }
 
   /** @return the last 12 hex digits. */
diff --git 
a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeerId.java 
b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeerId.java
index 5da89691b..afb7ef357 100644
--- a/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeerId.java
+++ b/ratis-common/src/main/java/org/apache/ratis/protocol/RaftPeerId.java
@@ -29,8 +29,8 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Supplier;
 
 /**
- * Id of Raft Peer which is globally unique.
- *
+ * ID of Raft Peer which is globally unique.
+ * <p>
  * This is a value-based class.
  */
 public final class RaftPeerId {
@@ -64,14 +64,13 @@ public final class RaftPeerId {
 
   private RaftPeerId(ByteString id) {
     this.id = Objects.requireNonNull(id, "id == null");
-    Preconditions.assertTrue(id.size() > 0, "id is empty.");
+    Preconditions.assertTrue(!id.isEmpty(), "id is empty.");
     this.idString = id.toString(StandardCharsets.UTF_8);
     this.raftPeerIdProto = JavaUtils.memoize(this::buildRaftPeerIdProto);
   }
 
   private RaftPeerIdProto buildRaftPeerIdProto() {
-    final RaftPeerIdProto.Builder builder = 
RaftPeerIdProto.newBuilder().setId(id);
-    return builder.build();
+    return RaftPeerIdProto.newBuilder().setId(id).build();
   }
 
   public RaftPeerIdProto getRaftPeerIdProto() {
diff --git 
a/ratis-common/src/main/java/org/apache/ratis/thirdparty/com/google/protobuf/ByteStringUtils.java
 
b/ratis-common/src/main/java/org/apache/ratis/thirdparty/com/google/protobuf/ByteStringUtils.java
new file mode 100644
index 000000000..85174979f
--- /dev/null
+++ 
b/ratis-common/src/main/java/org/apache/ratis/thirdparty/com/google/protobuf/ByteStringUtils.java
@@ -0,0 +1,30 @@
+/*
+ * 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.ratis.thirdparty.com.google.protobuf;
+
+/** Utilities for {@link ByteString}. */
+public interface ByteStringUtils {
+  /**
+   * Wrap the given array.
+   * Note that the content of the array must not be changed.
+   * Otherwise, it violates the immutability of {@link ByteString}.
+   */
+  static ByteString unsafeWrap(byte[] array) {
+    return ByteString.wrap(array);
+  }
+}
diff --git a/ratis-test/src/test/java/org/apache/ratis/protocol/TestRaftId.java 
b/ratis-test/src/test/java/org/apache/ratis/protocol/TestRaftId.java
index 30b7ed54f..6610b3d04 100644
--- a/ratis-test/src/test/java/org/apache/ratis/protocol/TestRaftId.java
+++ b/ratis-test/src/test/java/org/apache/ratis/protocol/TestRaftId.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -22,12 +22,31 @@ import 
org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.util.UUID;
+
 public class TestRaftId extends BaseTest {
   @Override
   public int getGlobalTimeoutSeconds() {
     return 1;
   }
 
+  @Test
+  public void testRaftId() {
+    assertRaftId(RaftId.ZERO_UUID, RaftId.ZERO_UUID_BYTESTRING);
+    assertRaftId(UUID.randomUUID(), null);
+  }
+
+  static void assertRaftId(UUID original, ByteString expected) {
+    final ByteString bytes = RaftId.toByteString(original);
+    if (expected != null) {
+      Assert.assertEquals(expected, bytes);
+    }
+    final UUID computed = RaftId.toUuid(bytes);
+
+    Assert.assertEquals(original, computed);
+    Assert.assertEquals(bytes, RaftId.toByteString(computed));
+  }
+
   @Test
   public void testClientId() {
     final ClientId id = ClientId.randomId();

Reply via email to