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

kturner pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo.git


The following commit(s) were added to refs/heads/main by this push:
     new c9d23aae5c improves ZooCache node tracking (#5286)
c9d23aae5c is described below

commit c9d23aae5ca472adf1346775cb6916e1c034da0d
Author: Keith Turner <ktur...@apache.org>
AuthorDate: Wed Feb 5 12:08:39 2025 -0500

    improves ZooCache node tracking (#5286)
    
    ZooCache was using nulls to track four distinct states about what it had
    discovered about a node in ZooKeeper.  Using nulls to track these
    discovered states was awful, so replaced the usage of nulls with a new
    enum to track the discovered state of a node.
    
    
    Co-authored-by: Kevin Rathbun <kevinrr...@gmail.com>
---
 .../org/apache/accumulo/core/zookeeper/ZcNode.java | 116 ++++++++++++++-------
 1 file changed, 76 insertions(+), 40 deletions(-)

diff --git a/core/src/main/java/org/apache/accumulo/core/zookeeper/ZcNode.java 
b/core/src/main/java/org/apache/accumulo/core/zookeeper/ZcNode.java
index 78f33c9068..c555ccf99b 100644
--- a/core/src/main/java/org/apache/accumulo/core/zookeeper/ZcNode.java
+++ b/core/src/main/java/org/apache/accumulo/core/zookeeper/ZcNode.java
@@ -20,7 +20,6 @@ package org.apache.accumulo.core.zookeeper;
 
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.atomic.AtomicLong;
 
 import com.google.common.base.Preconditions;
 
@@ -29,36 +28,50 @@ import com.google.common.base.Preconditions;
  * and children are obtained from zookeeper at different times. This class is 
structured so that
  * data can be obtained first and then children added later or visa veras.
  *
- * <p>
- * Four distinct states can be cached for a zookeeper node.
- * <ul>
- * <li>Can cache that a node does not exist in zookeeper. This state is 
represented by data, state,
- * and children all being null.</li>
- * <li>Can cache only the data for a zookeeper node. For this state data and 
stat are non-null while
- * children is null. Calling getChildren on node in this state will throw an 
exception.</li>
- * <li>Can cache only the children for a zookeeper node. For this state 
children is non-null while
- * data and stat are null. Calling getData or getStat on node in this state 
will throw an
- * exception.</li>
- * <li>Can cache the children and data for a zookeeper node. For this state 
data,stat, and children
- * are non-null.</li>
- * </ul>
- * <p>
- *
  */
 class ZcNode {
 
+  /**
+   * This enum represents what ZooCache has discovered about a given node in 
zookeeper so far.
+   */
+  enum Discovered {
+    /**
+     * An attempt was made to fetch data or children and ZooKeeper threw a 
NoNodeException. In this
+     * case ZooCache knows the node did not exist.
+     */
+    NON_EXISTENT,
+    /**
+     * ZooCache knows the node existed and what its children were because a 
successful call was made
+     * to ZooKeeper to get children. However zoocache has never requested the 
nodes data and does
+     * not know its data.
+     */
+    CHILDREN_ONLY,
+    /**
+     * ZooCache knows the node exists and what its data was because a 
successful call was made to
+     * ZooKeeper to get data. However zoocache has never requested the nodes 
children and does not
+     * know its children.
+     */
+    DATA_ONLY,
+    /**
+     * ZooCache knows the node existed and has made successful calls to 
ZooKeeper to get the nodes
+     * data and children.
+     */
+    CHILDREN_AND_DATA;
+
+  }
+
   private final byte[] data;
   private final ZcStat stat;
   private final List<String> children;
+  private final Discovered discovered;
 
   static final ZcNode NON_EXISTENT = new ZcNode();
 
-  private final AtomicLong accessCount = new AtomicLong(0);
-
   private ZcNode() {
     this.data = null;
     this.stat = null;
     this.children = null;
+    this.discovered = Discovered.NON_EXISTENT;
   }
 
   /**
@@ -68,11 +81,28 @@ class ZcNode {
   ZcNode(List<String> children, ZcNode existing) {
     Objects.requireNonNull(children);
     if (existing == null) {
+      this.discovered = Discovered.CHILDREN_ONLY;
       this.data = null;
       this.stat = null;
     } else {
-      this.data = existing.data;
-      this.stat = existing.stat;
+      switch (existing.discovered) {
+        case NON_EXISTENT:
+        case CHILDREN_ONLY:
+          Preconditions.checkArgument(existing.data == null && existing.stat 
== null);
+          this.discovered = Discovered.CHILDREN_ONLY;
+          this.data = null;
+          this.stat = null;
+          break;
+        case DATA_ONLY:
+        case CHILDREN_AND_DATA:
+          this.discovered = Discovered.CHILDREN_AND_DATA;
+          this.data = Objects.requireNonNull(existing.data);
+          this.stat = Objects.requireNonNull(existing.stat);
+          break;
+        default:
+          throw new IllegalStateException("Unknown enum " + 
existing.discovered);
+      }
+
     }
 
     this.children = List.copyOf(children);
@@ -83,31 +113,48 @@ class ZcNode {
    * stat.
    */
   ZcNode(byte[] data, ZcStat zstat, ZcNode existing) {
+
     this.data = Objects.requireNonNull(data);
     this.stat = Objects.requireNonNull(zstat);
+
     if (existing == null) {
+      this.discovered = Discovered.DATA_ONLY;
       this.children = null;
     } else {
-      this.children = existing.children;
+      switch (existing.discovered) {
+        case NON_EXISTENT:
+        case DATA_ONLY:
+          Preconditions.checkArgument(existing.children == null);
+          this.discovered = Discovered.DATA_ONLY;
+          this.children = null;
+          break;
+        case CHILDREN_ONLY:
+        case CHILDREN_AND_DATA:
+          this.discovered = Discovered.CHILDREN_AND_DATA;
+          this.children = Objects.requireNonNull(existing.children);
+          break;
+        default:
+          throw new IllegalStateException("Unknown enum " + 
existing.discovered);
+      }
     }
   }
 
   /**
    * @return the data if the node exists and the data was set OR return null 
when the node does not
    *         exist
-   * @throws IllegalStateException in the case where the node exists and the 
data was never set
+   * @throws IllegalStateException in the case where the node exists and the 
data was never set.
+   *         This is thrown when {@link #cachedData()} returns false.
    */
   byte[] getData() {
     Preconditions.checkState(cachedData());
-    ;
-    accessCount.incrementAndGet();
     return data;
   }
 
   /**
    * @return the stat if the node exists and the stat was set OR return null 
when the node does not
    *         exist
-   * @throws IllegalStateException in the case where the node exists and the 
data was never set
+   * @throws IllegalStateException in the case where the node exists and the 
data was never set.
+   *         This is thrown when {@link #cachedData()} returns false.
    */
   ZcStat getStat() {
     Preconditions.checkState(cachedData());
@@ -117,11 +164,11 @@ class ZcNode {
   /**
    * @return the children if the node exists and the children were set OR 
return null when the node
    *         does not exist exists
-   * @throws IllegalStateException in the case where the node exists and the 
children were never set
+   * @throws IllegalStateException in the case where the node exists and the 
children were never
+   *         set. This is thrown when {@link #cachedChildren()} returns false.
    */
   List<String> getChildren() {
     Preconditions.checkState(cachedChildren());
-    accessCount.incrementAndGet();
     return children;
   }
 
@@ -129,24 +176,13 @@ class ZcNode {
    * @return true if the node does not exists or it exists and children are 
cached.
    */
   boolean cachedChildren() {
-    return children != null || notExists();
+    return discovered != Discovered.DATA_ONLY;
   }
 
   /**
    * @return true if the node does not exists or it exists and data and stat 
cached.
    */
   boolean cachedData() {
-    return data != null || notExists();
-  }
-
-  /**
-   * @return true if the node does not exists in zookeeper
-   */
-  boolean notExists() {
-    return stat == null && data == null && children == null;
-  }
-
-  public long getAccessCount() {
-    return accessCount.get();
+    return discovered != Discovered.CHILDREN_ONLY;
   }
 }

Reply via email to