Skip to content
Merged
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
@@ -0,0 +1,64 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.internal.util.cache;

import java.util.function.Function;

/**
* Contract for internal caches.
* We call it "internal" Cache to disambiguate from the canonical entity 2LC, as that one is more commonly
* used by users and therefore of public knowledge.
* We highly prefer caches to be implemented by reputable caching libraries, this reduces our maintenance complexity
* (as maintaining a proper cache implementation is not at all trivial) and allows for people to experiment with
* various algorithms, including state-of-the-art that we might not be familiar with.
* For these reasons, we rely on this internal interface and encourage plugging in an external implementation;
* at the time of writing this we'll have a legacy implementation for backwards compatibility reasons but the general
* idea is to deprecate it and eventually require a third party implementation.
*/
public interface InternalCache<K, V> {

/**
* @return An estimate of the number of values contained in the cache.
*/
int heldElementsEstimate();

/**
* Attempt to read from the cache. Will return null on cache miss.
* It would typically be better to use {@link #computeIfAbsent(Object, Function)} instead.
* @param key
* @return
*/
V get(K key);

/**
* Stores a key/value pair into the cache. Storage is not guaranteed, as the implementation
* has liberty to cap internal storage or use various eviction strategies.
* @param key
* @param value
*/
void put(K key, V value);

/**
* Attempts to clear the content of the cache. Note that in some cache implementations this
* is not a trivial operation and should not be used on a performance critical path.
* Also note that thorough cleanup is not guaranteed:
* in some implementations it's a "best effort" strategy, or could be ignored altogether.
* Essentially it's useful as a hint that the client will no longer likely need to stored entries,
* so to save memory, if possible.
*/
void clear();

/**
* This should be the preferred main strategy to benefit from the cache: it allows to implement
* the general pattern of "try to read, or produce a value and then cache it" but avoiding
* efficiency issues that would be caused by accessing the cache multiple times, not least
* potentially a cache stampede, and concurrent need for generating the same value.
* @param key
* @param mappingFunction This function will be invoked to produce the value, and store it,
* if a matching existing value couldn't be loaded from the cache.
* @return Either the existing value, or the return from the provided function.
*/
V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.internal.util.cache;

import org.hibernate.service.Service;

/**
* Internal components can use this factory to create an efficient cache for internal purposes.
* The implementation is pluggable, therefore the exact eviction and sizing semantics are unspecified
* and responsibility of the implementation.
*/
public interface InternalCacheFactory extends Service {

<K,V> InternalCache<K,V> createInternalCache(int intendedApproximateSize);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.internal.util.cache;

final class InternalCacheFactoryImpl implements InternalCacheFactory {

@Override
public <K, V> InternalCache<K, V> createInternalCache(int intendedApproximateSize) {
return new LegacyInternalCacheImplementation( intendedApproximateSize );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.internal.util.cache;

import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.service.spi.ServiceRegistryImplementor;

import java.util.Map;

public class InternalCacheFactoryInitiator implements StandardServiceInitiator<InternalCacheFactory> {

/**
* Singleton access
*/
public static final InternalCacheFactoryInitiator INSTANCE = new InternalCacheFactoryInitiator();

private InternalCacheFactoryInitiator() {}

@Override
public InternalCacheFactory initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
return new InternalCacheFactoryImpl();
}

@Override
public Class<InternalCacheFactory> getServiceInitiated() {
return InternalCacheFactory.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.internal.util.cache;

import org.hibernate.internal.util.collections.BoundedConcurrentHashMap;

import java.io.Serializable;
import java.util.function.Function;

/**
* An implementation of {@link InternalCache} based on the deprecated {@link BoundedConcurrentHashMap}.
* @param <K>
* @param <V>
*/
final class LegacyInternalCacheImplementation<K,V> implements InternalCache<K,V>, Serializable {

private final BoundedConcurrentHashMap<K,V> map;

public LegacyInternalCacheImplementation(int intendedApproximateSize) {
map = new BoundedConcurrentHashMap<>(
intendedApproximateSize, 20, BoundedConcurrentHashMap.Eviction.LIRS );
}

@Override
public int heldElementsEstimate() {
return map.size();
}

@Override
public V get(K key) {
return map.get( key );
}

@Override
public void put(K key, V value) {
map.put( key, value );
}

@Override
public void clear() {
map.clear();
}

@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
return map.computeIfAbsent( key, mappingFunction );
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@
* @param <V> the type of mapped values
*
* @author Doug Lea
* @deprecated We shouldn't maintain a cache implementation in the Hibernate ORM codebase,
* so this will be eventually removed as we encourage to plug-in external implementations for all caching needs.
*/
@Deprecated(forRemoval = true)
public class BoundedConcurrentHashMap<K, V> extends AbstractMap<K, V>
implements ConcurrentMap<K, V>, Serializable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import java.util.function.Function;
import java.util.function.Supplier;

import org.hibernate.internal.util.collections.BoundedConcurrentHashMap;
import org.hibernate.internal.util.cache.InternalCache;
import org.hibernate.internal.util.cache.InternalCacheFactory;
import org.hibernate.query.QueryLogging;
import org.hibernate.query.hql.HqlTranslator;
import org.hibernate.query.spi.HqlInterpretation;
Expand Down Expand Up @@ -37,31 +38,31 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
/**
* the cache of the actual plans...
*/
private final BoundedConcurrentHashMap<Key, QueryPlan> queryPlanCache;
private final InternalCache<Key, QueryPlan> queryPlanCache;

private final ServiceRegistry serviceRegistry;
private final BoundedConcurrentHashMap<Object, HqlInterpretation<?>> hqlInterpretationCache;
private final BoundedConcurrentHashMap<String, ParameterInterpretation> nativeQueryParamCache;
private final InternalCache<Object, HqlInterpretation<?>> hqlInterpretationCache;
private final InternalCache<String, ParameterInterpretation> nativeQueryParamCache;

private StatisticsImplementor statistics;

public QueryInterpretationCacheStandardImpl(int maxQueryPlanCount, ServiceRegistry serviceRegistry) {
log.debugf( "Starting QueryInterpretationCache(%s)", maxQueryPlanCount );

this.queryPlanCache = new BoundedConcurrentHashMap<>( maxQueryPlanCount, 20, BoundedConcurrentHashMap.Eviction.LIRS );
this.hqlInterpretationCache = new BoundedConcurrentHashMap<>( maxQueryPlanCount, 20, BoundedConcurrentHashMap.Eviction.LIRS );
this.nativeQueryParamCache = new BoundedConcurrentHashMap<>( maxQueryPlanCount, 20, BoundedConcurrentHashMap.Eviction.LIRS );
final InternalCacheFactory cacheFactory = serviceRegistry.requireService( InternalCacheFactory.class );
this.queryPlanCache = cacheFactory.createInternalCache( maxQueryPlanCount );
this.hqlInterpretationCache = cacheFactory.createInternalCache( maxQueryPlanCount );
this.nativeQueryParamCache = cacheFactory.createInternalCache( maxQueryPlanCount );
this.serviceRegistry = serviceRegistry;
}

@Override
public int getNumberOfCachedHqlInterpretations() {
return hqlInterpretationCache.size();
return hqlInterpretationCache.heldElementsEstimate();
}

@Override
public int getNumberOfCachedQueryPlans() {
return queryPlanCache.size();
return queryPlanCache.heldElementsEstimate();
}

private StatisticsImplementor getStatistics() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator;
import org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformResolverInitiator;
import org.hibernate.event.internal.EntityCopyObserverFactoryInitiator;
import org.hibernate.internal.util.cache.InternalCacheFactoryInitiator;
import org.hibernate.loader.ast.internal.BatchLoaderFactoryInitiator;
import org.hibernate.persister.internal.PersisterClassResolverInitiator;
import org.hibernate.persister.internal.PersisterFactoryInitiator;
Expand Down Expand Up @@ -156,6 +157,9 @@ private static List<StandardServiceInitiator<?>> buildStandardServiceInitiatorLi
serviceInitiators.add( ParameterMarkerStrategyInitiator.INSTANCE );
serviceInitiators.add( BatchLoaderFactoryInitiator.INSTANCE );

// InternalCacheFactoryService
serviceInitiators.add( InternalCacheFactoryInitiator.INSTANCE );

serviceInitiators.trimToSize();

return unmodifiableList( serviceInitiators );
Expand Down