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

taklwu pushed a commit to branch HBASE-30018
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/HBASE-30018 by this push:
     new 09c627ae837 HBASE-30020 Introduce cache placement and admission policy 
API (#8184)
09c627ae837 is described below

commit 09c627ae8377b89459ef8f77a33332439a4174ed
Author: Vladimir Rodionov <[email protected]>
AuthorDate: Thu May 7 12:12:00 2026 -0700

    HBASE-30020 Introduce cache placement and admission policy API (#8184)
    
    Signed-off-by: Peter Somogyi <[email protected]>
    Signed-off-by: Tak Lon (Stephen) Wu <[email protected]>
    Signed-off-by: Wellington Chevreuil <[email protected]>
---
 .../hbase/io/hfile/cache/AdmissionDecision.java    |  89 ++++++++++
 .../hbase/io/hfile/cache/AdmissionPriority.java    |  42 +++++
 .../hfile/cache/CachePlacementAdmissionPolicy.java |  88 ++++++++++
 .../hbase/io/hfile/cache/CacheRequestContext.java  | 187 +++++++++++++++++++++
 .../hbase/io/hfile/cache/CacheWriteContext.java    | 166 ++++++++++++++++++
 .../hbase/io/hfile/cache/CacheWriteSource.java     |  57 +++++++
 .../DefaultHBaseCachePlacementAdmissionPolicy.java | 124 ++++++++++++++
 .../hbase/io/hfile/cache/PromotionAction.java      |  37 ++++
 .../hbase/io/hfile/cache/PromotionDecision.java    | 122 ++++++++++++++
 .../io/hfile/cache/RepresentationDecision.java     |  52 ++++++
 .../hadoop/hbase/io/hfile/cache/TierDecision.java  | 115 +++++++++++++
 11 files changed, 1079 insertions(+)

diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionDecision.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionDecision.java
new file mode 100644
index 00000000000..8e173c616f7
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionDecision.java
@@ -0,0 +1,89 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Result of cache admission evaluation.
+ */
[email protected]
+public final class AdmissionDecision {
+
+  private static final AdmissionDecision ADMIT = new AdmissionDecision(true, 
"ADMIT");
+
+  private static final AdmissionDecision REJECT = new AdmissionDecision(false, 
"REJECT");
+
+  private final boolean admit;
+  private final String reason;
+
+  private AdmissionDecision(boolean admit, String reason) {
+    this.admit = admit;
+    this.reason = reason;
+  }
+
+  /**
+   * Returns a normal-priority admission decision.
+   * @return admit decision
+   */
+  public static AdmissionDecision admit() {
+    return ADMIT;
+  }
+
+  /**
+   * Returns an admission decision with a reason.
+   * @param reason reason text
+   * @return admit decision
+   */
+  public static AdmissionDecision admit(String reason) {
+    return new AdmissionDecision(true, reason);
+  }
+
+  /**
+   * Returns a rejection decision. * @return reject decision
+   */
+  public static AdmissionDecision reject() {
+    return REJECT;
+  }
+
+  /**
+   * Returns a rejection decision with a reason.
+   * @param reason rejection reason
+   * @return reject decision
+   */
+  public static AdmissionDecision reject(String reason) {
+    return new AdmissionDecision(false, reason);
+  }
+
+  /**
+   * Returns whether the block should be admitted.
+   * @return true when admitted
+   */
+  public boolean isAdmitted() {
+    return admit;
+  }
+
+  /**
+   * Returns the decision reason.
+   * @return decision reason
+   */
+  public String getReason() {
+    return reason;
+  }
+
+}
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionPriority.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionPriority.java
new file mode 100644
index 00000000000..3157d98061e
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/AdmissionPriority.java
@@ -0,0 +1,42 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Priority hint associated with an admitted cache block.
+ */
[email protected]
+public enum AdmissionPriority {
+
+  /**
+   * Low-priority admission, suitable for opportunistic insertions such as 
prefetch.
+   */
+  LOW,
+
+  /**
+   * Normal-priority admission.
+   */
+  NORMAL,
+
+  /**
+   * High-priority admission, suitable for metadata-heavy or latency-sensitive 
blocks.
+   */
+  HIGH
+}
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CachePlacementAdmissionPolicy.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CachePlacementAdmissionPolicy.java
new file mode 100644
index 00000000000..7ce89fdceeb
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CachePlacementAdmissionPolicy.java
@@ -0,0 +1,88 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
+import org.apache.hadoop.hbase.io.hfile.Cacheable;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Policy interface for cache admission, tier placement, representation, and 
promotion.
+ * <p>
+ * This policy decides what should happen for a cache operation. It does not 
execute cache
+ * operations directly. Execution belongs to {@link CacheTopology} and storage 
belongs to
+ * {@link CacheEngine}.
+ * </p>
+ * <p>
+ * The policy may consult {@link CacheTopologyView} to inspect available tiers 
and engine capacity,
+ * but it must not mutate cache state.
+ * </p>
+ */
[email protected]
+public interface CachePlacementAdmissionPolicy {
+
+  /**
+   * Decides whether a block should be admitted into the cache.
+   * @param cacheKey     block cache key
+   * @param block        block to cache
+   * @param context      write context describing insertion source and hints
+   * @param priority     admission priority hint
+   * @param topologyView read-only topology view
+   * @return admission decision
+   */
+  AdmissionDecision shouldAdmit(BlockCacheKey cacheKey, Cacheable block, 
CacheWriteContext context,
+    AdmissionPriority priority, CacheTopologyView topologyView);
+
+  /**
+   * Selects the target tier for an admitted block.
+   * @param cacheKey     block cache key
+   * @param block        block to cache
+   * @param context      write context describing insertion source and hints
+   * @param topologyView read-only topology view
+   * @return tier decision
+   */
+  TierDecision selectTier(BlockCacheKey cacheKey, Cacheable block, 
CacheWriteContext context,
+    CacheTopologyView topologyView);
+
+  /**
+   * Selects the cached representation for an admitted block.
+   * <p>
+   * The initial compatibility policy should preserve the representation 
currently produced by
+   * existing HBase code paths.
+   * </p>
+   * @param cacheKey     block cache key
+   * @param block        block to cache
+   * @param context      write context describing insertion source and hints
+   * @param topologyView read-only topology view
+   * @return representation decision
+   */
+  RepresentationDecision selectRepresentation(BlockCacheKey cacheKey, 
Cacheable block,
+    CacheWriteContext context, CacheTopologyView topologyView);
+
+  /**
+   * Decides whether a cache hit should trigger promotion to another tier.
+   * @param cacheKey     block cache key
+   * @param block        cached block
+   * @param sourceTier   tier where the block was found
+   * @param context      read request context
+   * @param topologyView read-only topology view
+   * @return promotion decision
+   */
+  PromotionDecision shouldPromote(BlockCacheKey cacheKey, Cacheable block, 
CacheTier sourceTier,
+    CacheRequestContext context, CacheTopologyView topologyView);
+}
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheRequestContext.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheRequestContext.java
new file mode 100644
index 00000000000..17aee5265fd
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheRequestContext.java
@@ -0,0 +1,187 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import org.apache.hadoop.hbase.io.hfile.BlockType;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Context for cache lookup.
+ * <p>
+ * This object carries read-path cache lookup intent. It mirrors the existing 
low-allocation
+ * BlockCache request parameters while allowing future call sites to pass 
structured context.
+ * </p>
+ */
[email protected]
+public final class CacheRequestContext {
+
+  private final boolean caching;
+  private final boolean repeat;
+  private final boolean updateCacheMetrics;
+  private final BlockType blockType;
+  private final boolean compaction;
+  private final boolean prefetch;
+
+  private CacheRequestContext(Builder builder) {
+    this.caching = builder.caching;
+    this.repeat = builder.repeat;
+    this.updateCacheMetrics = builder.updateCacheMetrics;
+    this.blockType = builder.blockType;
+    this.compaction = builder.compaction;
+    this.prefetch = builder.prefetch;
+  }
+
+  /**
+   * Creates a new builder.
+   */
+  public static Builder newBuilder() {
+    return new Builder();
+  }
+
+  /**
+   * Returns whether caching is enabled for this request.
+   * @return true when caching is enabled
+   */
+  public boolean isCaching() {
+    return caching;
+  }
+
+  /**
+   * Returns whether this is a repeated lookup for the same block.
+   * @return true when this is a repeated lookup
+   */
+  public boolean isRepeat() {
+    return repeat;
+  }
+
+  /**
+   * Returns whether cache metrics should be updated.
+   * @return true when metrics should be updated
+   */
+  public boolean isUpdateCacheMetrics() {
+    return updateCacheMetrics;
+  }
+
+  /**
+   * Returns the expected block type, if known.
+   * @return block type, or null if unknown
+   */
+  public BlockType getBlockType() {
+    return blockType;
+  }
+
+  /**
+   * Returns whether this request is associated with compaction.
+   * @return true when compaction-related
+   */
+  public boolean isCompaction() {
+    return compaction;
+  }
+
+  /**
+   * Returns whether this request is associated with prefetch.
+   * @return true when prefetch-related
+   */
+  public boolean isPrefetch() {
+    return prefetch;
+  }
+
+  /**
+   * Builder for {@link CacheRequestContext}.
+   */
+  public static final class Builder {
+
+    private boolean caching;
+    private boolean repeat;
+    private boolean updateCacheMetrics = true;
+    private BlockType blockType;
+    private boolean compaction;
+    private boolean prefetch;
+
+    private Builder() {
+    }
+
+    /**
+     * Sets whether caching is enabled.
+     * @param caching true when caching is enabled
+     * @return this builder
+     */
+    public Builder setCaching(boolean caching) {
+      this.caching = caching;
+      return this;
+    }
+
+    /**
+     * Sets repeated-lookup status.
+     * @param repeat true when this is a repeated lookup
+     * @return this builder
+     */
+    public Builder setRepeat(boolean repeat) {
+      this.repeat = repeat;
+      return this;
+    }
+
+    /**
+     * Sets whether cache metrics should be updated.
+     * @param updateCacheMetrics true when metrics should be updated
+     * @return this builder
+     */
+    public Builder setUpdateCacheMetrics(boolean updateCacheMetrics) {
+      this.updateCacheMetrics = updateCacheMetrics;
+      return this;
+    }
+
+    /**
+     * Sets the expected block type.
+     * @param blockType expected block type
+     * @return this builder
+     */
+    public Builder setBlockType(BlockType blockType) {
+      this.blockType = blockType;
+      return this;
+    }
+
+    /**
+     * Sets compaction request status.
+     * @param compaction true when compaction-related
+     * @return this builder
+     */
+    public Builder setCompaction(boolean compaction) {
+      this.compaction = compaction;
+      return this;
+    }
+
+    /**
+     * Sets prefetch request status.
+     * @param prefetch true when prefetch-related
+     * @return this builder
+     */
+    public Builder setPrefetch(boolean prefetch) {
+      this.prefetch = prefetch;
+      return this;
+    }
+
+    /**
+     * Builds the context.
+     * @return cache request context
+     */
+    public CacheRequestContext build() {
+      return new CacheRequestContext(this);
+    }
+  }
+}
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteContext.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteContext.java
new file mode 100644
index 00000000000..e9b2f729f5b
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteContext.java
@@ -0,0 +1,166 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import org.apache.hadoop.hbase.io.hfile.BlockType.BlockCategory;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Context for cache insertion.
+ * <p>
+ * This object carries insertion intent from read-miss population, write path 
population, prefetch,
+ * compaction, or promotion. It is intentionally small and immutable.
+ * </p>
+ */
[email protected]
+public final class CacheWriteContext {
+
+  private final boolean inMemory;
+  private final boolean waitWhenCache;
+  private final boolean cacheCompressed;
+  private final BlockCategory blockCategory;
+  private final CacheWriteSource source;
+
+  private CacheWriteContext(Builder builder) {
+    this.inMemory = builder.inMemory;
+    this.waitWhenCache = builder.waitWhenCache;
+    this.cacheCompressed = builder.cacheCompressed;
+    this.blockCategory = builder.blockCategory;
+    this.source = builder.source;
+  }
+
+  /**
+   * Creates a new builder.
+   */
+  public static Builder newBuilder() {
+    return new Builder();
+  }
+
+  /**
+   * Returns whether this block should receive in-memory treatment.
+   * @return true when in-memory treatment is requested
+   */
+  public boolean isInMemory() {
+    return inMemory;
+  }
+
+  /**
+   * Returns whether insertion should wait for asynchronous cache acceptance.
+   * @return true when wait is requested
+   */
+  public boolean isWaitWhenCache() {
+    return waitWhenCache;
+  }
+
+  /**
+   * Returns whether compressed caching is requested.
+   * @return true when compressed caching is requested
+   */
+  public boolean isCacheCompressed() {
+    return cacheCompressed;
+  }
+
+  /**
+   * Returns the block category, if known.
+   * @return block category, or null if unknown
+   */
+  public BlockCategory getBlockCategory() {
+    return blockCategory;
+  }
+
+  /**
+   * Returns the source of this cache write.
+   * @return cache write source
+   */
+  public CacheWriteSource getSource() {
+    return source;
+  }
+
+  /**
+   * Builder for {@link CacheWriteContext}.
+   */
+  public static final class Builder {
+
+    private boolean inMemory;
+    private boolean waitWhenCache;
+    private boolean cacheCompressed;
+    private BlockCategory blockCategory;
+    private CacheWriteSource source = CacheWriteSource.READ_MISS;
+
+    private Builder() {
+    }
+
+    /**
+     * Sets in-memory treatment.
+     * @param inMemory true when in-memory treatment is requested
+     * @return this builder
+     */
+    public Builder setInMemory(boolean inMemory) {
+      this.inMemory = inMemory;
+      return this;
+    }
+
+    /**
+     * Sets wait-on-cache behavior.
+     * @param waitWhenCache true when insertion should wait
+     * @return this builder
+     */
+    public Builder setWaitWhenCache(boolean waitWhenCache) {
+      this.waitWhenCache = waitWhenCache;
+      return this;
+    }
+
+    /**
+     * Sets compressed cache preference.
+     * @param cacheCompressed true when compressed caching is requested
+     * @return this builder
+     */
+    public Builder setCacheCompressed(boolean cacheCompressed) {
+      this.cacheCompressed = cacheCompressed;
+      return this;
+    }
+
+    /**
+     * Sets the block category.
+     * @param blockCategory block category
+     * @return this builder
+     */
+    public Builder setBlockCategory(BlockCategory blockCategory) {
+      this.blockCategory = blockCategory;
+      return this;
+    }
+
+    /**
+     * Sets the cache write source.
+     * @param source cache write source
+     * @return this builder
+     */
+    public Builder setSource(CacheWriteSource source) {
+      this.source = source;
+      return this;
+    }
+
+    /**
+     * Builds the context.
+     * @return cache write context
+     */
+    public CacheWriteContext build() {
+      return new CacheWriteContext(this);
+    }
+  }
+}
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteSource.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteSource.java
new file mode 100644
index 00000000000..2c98d9d7c94
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/CacheWriteSource.java
@@ -0,0 +1,57 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Source of a cache insertion request.
+ * <p>
+ * This value describes where a cache population request originated. It does 
not replace existing
+ * {@code CacheConfig} decisions. Callers should invoke cache population only 
after existing HBase
+ * logic has determined that the block is eligible for caching.
+ * </p>
+ */
[email protected]
+public enum CacheWriteSource {
+
+  /**
+   * Cache population after a read miss.
+   */
+  READ_MISS,
+
+  /**
+   * Cache population during flush output generation.
+   */
+  FLUSH,
+
+  /**
+   * Cache population during compaction output generation.
+   */
+  COMPACTION,
+
+  /**
+   * Cache population by prefetch.
+   */
+  PREFETCH,
+
+  /**
+   * Cache population caused by promotion from another tier.
+   */
+  PROMOTION
+}
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/DefaultHBaseCachePlacementAdmissionPolicy.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/DefaultHBaseCachePlacementAdmissionPolicy.java
new file mode 100644
index 00000000000..039c620a33d
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/DefaultHBaseCachePlacementAdmissionPolicy.java
@@ -0,0 +1,124 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import java.util.Objects;
+import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
+import org.apache.hadoop.hbase.io.hfile.BlockType;
+import org.apache.hadoop.hbase.io.hfile.Cacheable;
+import org.apache.hadoop.hbase.io.hfile.HFileBlock;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Default compatibility policy that preserves current HBase block cache 
behavior.
+ * <p>
+ * This policy is intentionally conservative. It admits cache insertion 
requests by default, assumes
+ * existing {@code CacheConfig} logic has already decided whether cache 
population should be
+ * attempted, preserves the current HBase block representation, and maps 
metadata-like blocks to L1
+ * and data blocks to L2 when a tiered topology is available.
+ * </p>
+ */
[email protected]
+public class DefaultHBaseCachePlacementAdmissionPolicy implements 
CachePlacementAdmissionPolicy {
+
+  @Override
+  public AdmissionDecision shouldAdmit(BlockCacheKey cacheKey, Cacheable block,
+    CacheWriteContext context, AdmissionPriority priority, CacheTopologyView 
topologyView) {
+    Objects.requireNonNull(cacheKey, "cacheKey must not be null");
+    Objects.requireNonNull(block, "block must not be null");
+    Objects.requireNonNull(context, "context must not be null");
+    Objects.requireNonNull(priority, "priority must not be null");
+    Objects.requireNonNull(topologyView, "topologyView must not be null");
+
+    return AdmissionDecision.admit();
+  }
+
+  @Override
+  public TierDecision selectTier(BlockCacheKey cacheKey, Cacheable block, 
CacheWriteContext context,
+    CacheTopologyView topologyView) {
+    Objects.requireNonNull(cacheKey, "cacheKey must not be null");
+    Objects.requireNonNull(block, "block must not be null");
+    Objects.requireNonNull(context, "context must not be null");
+    Objects.requireNonNull(topologyView, "topologyView must not be null");
+    /*
+     * Default compatibility placement prefers metadata/index/bloom blocks in 
L1 and data blocks in
+     * L2 when both tiers are available, but falls back to any available tier 
rather than rejecting
+     * placement.
+     */
+    if (topologyView.getType() == CacheTopologyType.SINGLE) {
+      return TierDecision.single(CacheTier.SINGLE);
+    }
+
+    if (isMetaOrIndexBlock(block)) {
+      if (topologyView.getEngine(CacheTier.L1).isPresent()) {
+        return TierDecision.single(CacheTier.L1);
+      }
+      if (topologyView.getEngine(CacheTier.L2).isPresent()) {
+        return TierDecision.single(CacheTier.L2);
+      }
+      return TierDecision.none();
+    }
+
+    if (topologyView.getEngine(CacheTier.L2).isPresent()) {
+      return TierDecision.single(CacheTier.L2);
+    }
+    if (topologyView.getEngine(CacheTier.L1).isPresent()) {
+      return TierDecision.single(CacheTier.L1);
+    }
+
+    return TierDecision.none();
+  }
+
+  @Override
+  public RepresentationDecision selectRepresentation(BlockCacheKey cacheKey, 
Cacheable block,
+    CacheWriteContext context, CacheTopologyView topologyView) {
+    Objects.requireNonNull(cacheKey, "cacheKey must not be null");
+    Objects.requireNonNull(block, "block must not be null");
+    Objects.requireNonNull(context, "context must not be null");
+    Objects.requireNonNull(topologyView, "topologyView must not be null");
+
+    return RepresentationDecision.CURRENT_HBASE_DEFAULT;
+  }
+
+  @Override
+  public PromotionDecision shouldPromote(BlockCacheKey cacheKey, Cacheable 
block,
+    CacheTier sourceTier, CacheRequestContext context, CacheTopologyView 
topologyView) {
+    Objects.requireNonNull(cacheKey, "cacheKey must not be null");
+    Objects.requireNonNull(block, "block must not be null");
+    Objects.requireNonNull(sourceTier, "sourceTier must not be null");
+    Objects.requireNonNull(context, "context must not be null");
+    Objects.requireNonNull(topologyView, "topologyView must not be null");
+    /*
+     * DefaultHBaseCachePlacementAdmissionPolicy should preserve existing 
placement behavior. If
+     * meta/index/bloom blocks belong in L1, they should be inserted into L1 
when cached. If data
+     * blocks belong in L2, promoting them to L1 on hit would change current 
behavior and may
+     * pollute L1. Therefore the compatibility policy should not promote by 
default.
+     */
+    return PromotionDecision.none();
+
+  }
+
+  private static boolean isMetaOrIndexBlock(Cacheable block) {
+    if (!(block instanceof HFileBlock)) {
+      return false;
+    }
+
+    BlockType blockType = ((HFileBlock) block).getBlockType();
+    return blockType != null && blockType.getCategory() != 
BlockType.BlockCategory.DATA;
+  }
+}
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionAction.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionAction.java
new file mode 100644
index 00000000000..94feb91f3d3
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionAction.java
@@ -0,0 +1,37 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Promotion action selected by cache placement policy.
+ */
[email protected]
+public enum PromotionAction {
+
+  /**
+   * Do not promote the block.
+   */
+  NONE,
+
+  /**
+   * Promote the block to another tier.
+   */
+  PROMOTE
+}
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionDecision.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionDecision.java
new file mode 100644
index 00000000000..87e4cf1584b
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/PromotionDecision.java
@@ -0,0 +1,122 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import java.util.Objects;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Decision describing whether and how a cache hit should trigger promotion.
+ * <p>
+ * This decision is produced by {@link CachePlacementAdmissionPolicy} and 
executed by
+ * {@link CacheTopology}. The policy decides whether promotion should happen 
and which tier should
+ * receive the promoted block. The topology decides how promotion is performed 
for its semantics,
+ * for example copy for inclusive topology or move for exclusive topology.
+ * </p>
+ * <p>
+ * This class is immutable. The no-promotion decision is represented by {@link 
#none()}. Promotion
+ * decisions must include a non-null target tier.
+ * </p>
+ */
[email protected]
+public final class PromotionDecision {
+
+  private static final PromotionDecision NONE =
+    new PromotionDecision(PromotionAction.NONE, null, false);
+
+  private final PromotionAction action;
+  private final CacheTier targetTier;
+  private final boolean asynchronous;
+
+  private PromotionDecision(PromotionAction action, CacheTier targetTier, 
boolean asynchronous) {
+    this.action = Objects.requireNonNull(action, "action must not be null");
+    this.targetTier = targetTier;
+    this.asynchronous = asynchronous;
+  }
+
+  /**
+   * Returns a decision that performs no promotion.
+   * @return no-promotion decision
+   */
+  public static PromotionDecision none() {
+    return NONE;
+  }
+
+  /**
+   * Returns a decision to promote the block to the specified target tier.
+   * <p>
+   * The target tier must not be null. The actual promotion mechanics are 
topology-specific. For
+   * example, an inclusive topology may copy the block into the target tier 
while an exclusive
+   * topology may move the block.
+   * </p>
+   * @param targetTier   target tier for promotion
+   * @param asynchronous whether promotion may be performed asynchronously
+   * @return promotion decision
+   */
+  public static PromotionDecision promoteTo(CacheTier targetTier, boolean 
asynchronous) {
+    Objects.requireNonNull(targetTier, "targetTier must not be null");
+    return new PromotionDecision(PromotionAction.PROMOTE, targetTier, 
asynchronous);
+  }
+
+  /**
+   * Returns the promotion action.
+   * @return promotion action
+   */
+  public PromotionAction getAction() {
+    return action;
+  }
+
+  /**
+   * Returns whether this decision requests promotion.
+   * @return true when promotion is requested
+   */
+  public boolean shouldPromote() {
+    return action == PromotionAction.PROMOTE;
+  }
+
+  /**
+   * Returns whether this decision has a target tier.
+   * @return true when target tier is available
+   */
+  public boolean hasTargetTier() {
+    return targetTier != null;
+  }
+
+  /**
+   * Returns the target tier for promotion.
+   * <p>
+   * This method is valid only when {@link #shouldPromote()} returns true.
+   * </p>
+   * @return target tier
+   * @throws IllegalStateException if this decision does not request promotion
+   */
+  public CacheTier getTargetTier() {
+    if (targetTier == null) {
+      throw new IllegalStateException("Promotion target tier is not 
available");
+    }
+    return targetTier;
+  }
+
+  /**
+   * Returns whether promotion may be executed asynchronously.
+   * @return true when asynchronous promotion is allowed
+   */
+  public boolean isAsynchronous() {
+    return asynchronous;
+  }
+}
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/RepresentationDecision.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/RepresentationDecision.java
new file mode 100644
index 00000000000..e30ba8e7172
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/RepresentationDecision.java
@@ -0,0 +1,52 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Decision describing the cached representation for an admitted block.
+ */
[email protected]
+public enum RepresentationDecision {
+
+  /**
+   * Preserve the representation already produced by current HBase code paths.
+   */
+
+  CURRENT_HBASE_DEFAULT,
+
+  /**
+   * Prefer packed or serialized representation.
+   */
+
+  PACKED,
+
+  /**
+   * Prefer unpacked or ready-to-use representation.
+   */
+
+  UNPACKED,
+
+  /**
+   * Let the target cache engine choose its preferred representation.
+   */
+
+  ENGINE_DEFAULT
+
+}
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TierDecision.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TierDecision.java
new file mode 100644
index 00000000000..fb0434b0ff7
--- /dev/null
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/cache/TierDecision.java
@@ -0,0 +1,115 @@
+/*
+ * 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.hbase.io.hfile.cache;
+
+import java.util.List;
+import java.util.Objects;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * Decision describing the target cache tiers for a cache insertion.
+ * <p>
+ * This decision explicitly lists the {@link CacheTier}s that should receive 
the block. It replaces
+ * implicit or topology-derived placement rules with a clear, ordered set of 
target tiers.
+ * </p>
+ * <p>
+ * The order of tiers is significant and should reflect placement priority. 
For example, a policy
+ * may return {@code [L1, L2]} to indicate that the block should be stored in 
L1 first and also
+ * written to L2 (e.g. for inclusive topologies).
+ * </p>
+ * <p>
+ * An empty decision indicates that the block should not be stored in any tier.
+ * </p>
+ * <p>
+ * This class is intentionally simple and immutable. It does not encode 
topology-specific behavior
+ * (e.g. inclusive vs exclusive). The {@link CacheTopology} is responsible for 
interpreting and
+ * executing this decision.
+ * </p>
+ */
[email protected]
+public final class TierDecision {
+
+  private static final TierDecision NONE = new TierDecision(List.of());
+
+  private final List<CacheTier> tiers;
+
+  private TierDecision(List<CacheTier> tiers) {
+    this.tiers = tiers;
+  }
+
+  /**
+   * Returns a decision that does not place the block into any cache tier.
+   * @return empty tier decision
+   */
+  public static TierDecision none() {
+    return NONE;
+  }
+
+  /**
+   * Returns a decision targeting a single cache tier.
+   * @param tier target tier
+   * @return tier decision for a single tier
+   */
+  public static TierDecision single(CacheTier tier) {
+    Objects.requireNonNull(tier, "tier must not be null");
+    return new TierDecision(List.of(tier));
+  }
+
+  /**
+   * Returns a decision targeting multiple cache tiers.
+   * <p>
+   * The provided list must not be null, must not contain null elements, and 
will be copied to
+   * preserve immutability.
+   * </p>
+   * @param tiers ordered list of target tiers
+   * @return tier decision for multiple tiers, or {@link #none()} if empty
+   */
+  public static TierDecision multiple(List<CacheTier> tiers) {
+    Objects.requireNonNull(tiers, "tiers must not be null");
+
+    if (tiers.isEmpty()) {
+      return NONE;
+    }
+
+    for (CacheTier tier : tiers) {
+      Objects.requireNonNull(tier, "tier in tiers must not be null");
+    }
+
+    return new TierDecision(List.copyOf(tiers));
+  }
+
+  /**
+   * Returns the ordered list of target tiers.
+   * <p>
+   * The returned list is immutable. The order reflects placement priority and 
may be interpreted by
+   * the topology when executing the decision.
+   * </p>
+   * @return ordered list of target tiers
+   */
+  public List<CacheTier> getTiers() {
+    return tiers;
+  }
+
+  /**
+   * Returns whether this decision contains no target tiers.
+   * @return true when no tiers are selected
+   */
+  public boolean isEmpty() {
+    return tiers.isEmpty();
+  }
+}


Reply via email to