Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@
*/
package org.apache.hadoop.hbase.io.hfile;

import java.util.Objects;
import java.util.Optional;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
import org.apache.hadoop.hbase.conf.ConfigurationManager;
import org.apache.hadoop.hbase.conf.PropagatingConfigurationObserver;
import org.apache.hadoop.hbase.io.ByteBuffAllocator;
import org.apache.hadoop.hbase.io.hfile.BlockType.BlockCategory;
import org.apache.hadoop.hbase.io.hfile.cache.BlockCacheBackedCacheAccessService;
import org.apache.hadoop.hbase.io.hfile.cache.BlockCacheBackedCacheEngine;
import org.apache.hadoop.hbase.io.hfile.cache.CacheAccessService;
import org.apache.hadoop.hbase.io.hfile.cache.CacheAccessServices;
import org.apache.hadoop.hbase.io.hfile.cache.CacheEngine;
import org.apache.hadoop.hbase.io.hfile.cache.CacheTier;
import org.apache.hadoop.hbase.io.hfile.cache.CacheTopology;
import org.apache.hadoop.hbase.io.hfile.cache.CacheTopologyType;
import org.apache.hadoop.hbase.io.hfile.cache.TopologyBackedCacheAccessService;
import org.apache.yetus.audience.InterfaceAudience;
Expand Down Expand Up @@ -221,12 +225,52 @@ public CacheConfig(Configuration conf, ColumnFamilyDescriptor family, CacheAcces
initFromConf(conf, family);
this.byteBuffAllocator = byteBuffAllocator;
this.cacheAccessService = service != null ? service : CacheAccessServices.disabled();
this.blockCache = service instanceof BlockCacheBackedCacheAccessService
? ((BlockCacheBackedCacheAccessService) service).getBlockCache()
: null;
/*
* Preserve the legacy BlockCache reference only when the supplied service is a direct
* single-tier BlockCache adapter. Multi-tier combined caches are represented by
* TopologyBackedCacheAccessService and intentionally do not expose a single legacy BlockCache
* through this field. Callers that need cache behavior or diagnostics should use
* cacheAccessService-level capabilities instead of relying on this legacy field.
*/
this.blockCache = unwrapSingleLegacyBlockCache(this.cacheAccessService);

}

/**
* Extracts the legacy {@link BlockCache} from a cache access service when that service is backed
* by a single {@link BlockCacheBackedCacheEngine}.
* <p>
* This method exists only to preserve compatibility with legacy {@link CacheConfig} callers that
* still use the {@link BlockCache} field. The active cache access path remains
* {@link CacheAccessService}. Code that needs cache behavior or diagnostics should use
* {@link CacheAccessService} capabilities instead of depending on the legacy block cache field.
* </p>
* @param cacheAccessService cache access service to inspect
* @return wrapped legacy block cache when available; otherwise {@code null}
* @throws NullPointerException if {@code cacheAccessService} is {@code null}
*/
private static BlockCache unwrapSingleLegacyBlockCache(CacheAccessService cacheAccessService) {
Objects.requireNonNull(cacheAccessService, "cacheAccessService must not be null");

if (!(cacheAccessService instanceof TopologyBackedCacheAccessService)) {
return null;
}

TopologyBackedCacheAccessService topologyBackedService =
(TopologyBackedCacheAccessService) cacheAccessService;
CacheTopology topology = topologyBackedService.getTopology();

if (topology.getType() != CacheTopologyType.SINGLE_TIER) {
return null;
}

Optional<CacheEngine> engine = topology.getEngine(CacheTier.L1);
if (!engine.isPresent() || !(engine.get() instanceof BlockCacheBackedCacheEngine)) {
return null;
}
return ((BlockCacheBackedCacheEngine) engine.get()).getBlockCache();
}

/**
* Create a cache configuration using the specified configuration object and family descriptor.
* @param conf hbase configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.apache.hadoop.hbase.io.hfile.BlockCacheFactory;
import org.apache.hadoop.hbase.io.hfile.CachedBlock;
import org.apache.hadoop.hbase.io.hfile.CombinedBlockCache;
import org.apache.hadoop.hbase.io.hfile.InclusiveCombinedBlockCache;
import org.apache.yetus.audience.InterfaceAudience;

/**
Expand All @@ -47,26 +48,37 @@ private CacheAccessServices() {
}

/**
* Creates a cache access service backed by an existing block cache.
* Creates a {@link CacheAccessService} for the supplied legacy {@link BlockCache}.
* <p>
* For regular {@link BlockCache} implementations, this returns a legacy
* {@link BlockCacheBackedCacheAccessService}. For {@link CombinedBlockCache}, this returns a
* topology-backed service using {@link TieredExclusiveTopology}. This moves combined L1/L2
* orchestration to the new topology layer while keeping the existing combined block cache object
* available for legacy {@link BlockCache}-facing APIs.
* All legacy block caches are adapted through {@link TopologyBackedCacheAccessService}. Plain
* single-tier block caches are represented by {@link SingleTierTopology}. Exclusive combined
* caches are represented by {@link TieredExclusiveTopology}. Inclusive combined caches are
* represented by {@link TieredInclusiveTopology}.
* </p>
* @param blockCache block cache to expose through {@link CacheAccessService}
* @return cache access service
* <p>
* {@link InclusiveCombinedBlockCache} is checked before {@link CombinedBlockCache} because the
* inclusive variant has different residency, promotion, and eviction semantics. Routing it
* through the exclusive topology would be incorrect.
* </p>
* @param blockCache legacy block cache to adapt
* @return topology-backed cache access service for the supplied block cache
* @throws NullPointerException if {@code blockCache} is {@code null}
*/

public static CacheAccessService fromBlockCache(BlockCache blockCache) {
Objects.requireNonNull(blockCache, "blockCache must not be null");

if (blockCache instanceof InclusiveCombinedBlockCache) {
return TopologyBackedCacheAccessServices
.fromInclusiveCombinedBlockCache((InclusiveCombinedBlockCache) blockCache);
}

if (blockCache instanceof CombinedBlockCache) {
return TopologyBackedCacheAccessServices
.fromCombinedBlockCache((CombinedBlockCache) blockCache);
}
return new BlockCacheBackedCacheAccessService(blockCache);

return TopologyBackedCacheAccessServices.fromSingleBlockCache("single", blockCache,
DefaultHBaseCachePlacementAdmissionPolicy.INSTANCE);
}

/**
Expand All @@ -80,7 +92,7 @@ public static CacheAccessService fromBlockCache(BlockCache blockCache) {
* The method delegates block-cache construction to
* {@link BlockCacheFactory#createBlockCache(Configuration)}. If the legacy factory creates a
* {@link BlockCache}, the returned service is backed by that cache through
* {@link BlockCacheBackedCacheAccessService}. If the legacy factory does not create a cache, this
* {@link TopologyBackedCacheAccessService}. If the legacy factory does not create a cache, this
* method returns the disabled/no-op cache access service.
* </p>
* <p>
Expand Down Expand Up @@ -140,27 +152,13 @@ public static CacheAccessService disabled() {
* @return optional iterable cached-block view
* @throws NullPointerException if {@code cacheAccessService} is {@code null}
*/
@SuppressWarnings("unchecked")
// public static Optional<Iterable<CachedBlock>>
// asCachedBlockIterable(CacheAccessService cacheAccessService) {
// Objects.requireNonNull(cacheAccessService, "cacheAccessService must not be null");
// if (cacheAccessService instanceof Iterable) {
// return Optional.of((Iterable<CachedBlock>) cacheAccessService);
// }
// return Optional.empty();
// }

public static Optional<Iterable<CachedBlock>> asCachedBlockIterable(CacheAccessService service) {
Objects.requireNonNull(service, "service must not be null");

if (service instanceof TopologyBackedCacheAccessService) {
return ((TopologyBackedCacheAccessService) service).asCachedBlockIterable();
}

if (service instanceof BlockCacheBackedCacheAccessService) {
return Optional.of(((BlockCacheBackedCacheAccessService) service).getBlockCache());
}

return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public enum CacheTopologyType {
/**
* A topology with a single cache engine.
*/
SINGLE,
SINGLE_TIER,

/**
* A tiered topology where a block normally resides in only one tier at a time.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
@InterfaceAudience.Private
public class DefaultHBaseCachePlacementAdmissionPolicy implements CachePlacementAdmissionPolicy {

public static final DefaultHBaseCachePlacementAdmissionPolicy INSTANCE =
new DefaultHBaseCachePlacementAdmissionPolicy();

@Override
public AdmissionDecision shouldAdmit(BlockCacheKey cacheKey, Cacheable block,
CacheWriteContext context, AdmissionPriority priority, CacheTopologyView topologyView) {
Expand All @@ -60,7 +63,7 @@ public TierDecision selectTier(BlockCacheKey cacheKey, Cacheable block, CacheWri
* L2 when both tiers are available, but falls back to any available tier rather than rejecting
* placement.
*/
if (topologyView.getType() == CacheTopologyType.SINGLE) {
if (topologyView.getType() == CacheTopologyType.SINGLE_TIER) {
return TierDecision.single(CacheTier.SINGLE);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public String getName() {

@Override
public CacheTopologyType getType() {
return CacheTopologyType.SINGLE;
return CacheTopologyType.SINGLE_TIER;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* 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.Collections;
import java.util.List;
import java.util.Optional;
import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
import org.apache.hadoop.hbase.io.hfile.CacheStats;
import org.apache.hadoop.hbase.io.hfile.Cacheable;
import org.apache.yetus.audience.InterfaceAudience;

/**
* Single-tier cache topology.
* <p>
* A single-tier topology contains exactly one cache engine. It is used to represent legacy
* single-tier {@code BlockCache} implementations inside the topology-backed cache access framework.
* Unlike tiered topologies, this topology does not perform tier orchestration, promotion, or
* demotion. All cache operations are directed to the single L1 engine.
* </p>
* <p>
* This topology allows plain block caches to use the same {@link TopologyBackedCacheAccessService}
* path as combined caches while preserving the existing cache implementation underneath.
* </p>
*/
@InterfaceAudience.Private
public class SingleTierTopology implements CacheTopology {

private final String name;
private final CacheEngine engine;
private final CacheTopologyView view;

/**
* Creates a single-tier cache topology.
* @param name topology name used for diagnostics
* @param engine cache engine backing the single tier
*/
public SingleTierTopology(String name, CacheEngine engine) {
this.name = name;
this.engine = engine;
this.view = new CacheTopologyView(this);
}

/**
* Returns the diagnostic name of this topology.
* @return topology name
*/
@Override
public String getName() {
return name;
}

/**
* Returns the topology type.
* @return {@link CacheTopologyType#SINGLE_TIER}
*/
@Override
public CacheTopologyType getType() {
return CacheTopologyType.SINGLE_TIER;
}

/**
* Returns the cache engines that participate in this topology.
* @return singleton list containing the single cache engine
*/
@Override
public List<CacheEngine> getEngines() {
return Collections.singletonList(engine);
}

/**
* Returns the tiers available in this topology.
* @return singleton list containing {@link CacheTier#L1}
*/
@Override
public List<CacheTier> getTiers() {
return Collections.singletonList(CacheTier.L1);
}

/**
* Returns the cache engine associated with the requested tier.
* @param tier cache tier to resolve
* @return the single cache engine for {@link CacheTier#L1}; otherwise {@link Optional#empty()}
*/
@Override
public Optional<CacheEngine> getEngine(CacheTier tier) {
switch (tier) {
case L1:
return Optional.of(engine);
default:
return Optional.empty();
}
}

/**
* Returns the topology view associated with this topology.
* @return topology view
*/
@Override
public CacheTopologyView getView() {
return view;
}

/**
* Returns cache statistics for the single cache engine.
* @return cache statistics exposed by the backing engine
*/
@Override
public CacheStats getStats() {
return engine.getStats();
}

/**
* Attempts to promote a block within this topology.
* <p>
* Single-tier topology has no higher or lower tier, so promotion is not supported. The method
* returns {@code false} without modifying the cache.
* </p>
* @param cacheKey cache key identifying the block
* @param block cached block
* @param sourceEngine source cache engine
* @param targetEngine target cache engine
* @return {@code false} because single-tier topology does not support promotion
*/
@Override
public boolean promote(BlockCacheKey cacheKey, Cacheable block, CacheEngine sourceEngine,
CacheEngine targetEngine) {
return false;
}

/**
* Attempts to demote a block within this topology.
* <p>
* Single-tier topology has no lower tier, so demotion is not supported. The method returns
* {@code false} without modifying the cache.
* </p>
* @param cacheKey cache key identifying the block
* @param block cached block
* @param sourceEngine source cache engine
* @param targetEngine target cache engine
* @return {@code false} because single-tier topology does not support demotion
*/
@Override
public boolean demote(BlockCacheKey cacheKey, Cacheable block, CacheEngine sourceEngine,
CacheEngine targetEngine) {
return false;
}

/**
* Shuts down the single cache engine.
*/
@Override
public void shutdown() {
engine.shutdown();
}
}
Loading
Loading