HHH-14691 Small optimisation for updating Query Cache Statistics
This commit is contained in:
parent
e91901946a
commit
916849a8af
|
@ -563,24 +563,21 @@ public class StatisticsImpl implements StatisticsImplementor, Service, Manageabl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CacheRegionStatisticsImpl getQueryRegionStatistics(String regionName) {
|
public CacheRegionStatisticsImpl getQueryRegionStatistics(final String regionName) {
|
||||||
final CacheRegionStatisticsImpl existing = l2CacheStatsMap.get( regionName );
|
return l2CacheStatsMap.getOrCompute( regionName, this::computeQueryRegionStatistics );
|
||||||
if ( existing != null ) {
|
|
||||||
return existing;
|
|
||||||
}
|
|
||||||
|
|
||||||
final QueryResultsCache regionAccess = cache
|
|
||||||
.getQueryResultsCacheStrictly( regionName );
|
|
||||||
if ( regionAccess == null ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return l2CacheStatsMap.getOrCompute(
|
|
||||||
regionName,
|
|
||||||
s -> new CacheRegionStatisticsImpl( regionAccess.getRegion() )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CacheRegionStatisticsImpl computeQueryRegionStatistics(final String regionName) {
|
||||||
|
final QueryResultsCache regionAccess = cache.getQueryResultsCacheStrictly( regionName );
|
||||||
|
if ( regionAccess == null ) {
|
||||||
|
return null; //this null value will be cached
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new CacheRegionStatisticsImpl( regionAccess.getRegion() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CacheRegionStatisticsImpl getCacheRegionStatistics(String regionName) {
|
public CacheRegionStatisticsImpl getCacheRegionStatistics(String regionName) {
|
||||||
if ( ! secondLevelCacheEnabled ) {
|
if ( ! secondLevelCacheEnabled ) {
|
||||||
|
|
|
@ -25,7 +25,8 @@ import org.hibernate.internal.util.collections.BoundedConcurrentHashMap;
|
||||||
*/
|
*/
|
||||||
final class StatsNamedContainer<V> {
|
final class StatsNamedContainer<V> {
|
||||||
|
|
||||||
private final ConcurrentMap<String,V> map;
|
private final ConcurrentMap<String,Object> map;
|
||||||
|
private final static Object NULL_TOKEN = new Object();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a bounded container - based on BoundedConcurrentHashMap
|
* Creates a bounded container - based on BoundedConcurrentHashMap
|
||||||
|
@ -63,33 +64,39 @@ final class StatsNamedContainer<V> {
|
||||||
* sure the function is invoked at most once: we don't need this guarantee, and prefer to reduce risk of blocking.
|
* sure the function is invoked at most once: we don't need this guarantee, and prefer to reduce risk of blocking.
|
||||||
*/
|
*/
|
||||||
public V getOrCompute(final String key, final Function<String, V> function) {
|
public V getOrCompute(final String key, final Function<String, V> function) {
|
||||||
final V v1 = map.get( key );
|
final Object v1 = map.get( key );
|
||||||
if ( v1 != null ) {
|
if ( v1 != null ) {
|
||||||
return v1;
|
if ( v1 == NULL_TOKEN ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (V) v1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final V v2 = function.apply( key );
|
final V v2 = function.apply( key );
|
||||||
//Occasionally a function might return null. We can't store a null in the CHM,
|
|
||||||
// so a placeholder would be required to implement that, but we prefer to just keep this
|
|
||||||
// situation as slightly sub-optimal so to not make the code more complex just to handle the exceptional case:
|
|
||||||
// null values are assumed to be rare enough for this not being worth it.
|
|
||||||
if ( v2 == null ) {
|
if ( v2 == null ) {
|
||||||
|
map.put( key, NULL_TOKEN );
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final V v3 = map.putIfAbsent( key, v2 );
|
final Object v3 = map.putIfAbsent( key, v2 );
|
||||||
if ( v3 == null ) {
|
if ( v3 == null ) {
|
||||||
return v2;
|
return v2;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return v3;
|
return (V) v3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public V get(final String key) {
|
public V get(final String key) {
|
||||||
return map.get( key );
|
final Object o = map.get( key );
|
||||||
|
if ( o == NULL_TOKEN) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (V) o;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,21 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.stat.internal;
|
package org.hibernate.stat.internal;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
@TestForIssue(jiraKey = "HHH-13645")
|
@TestForIssue(jiraKey = "HHH-13645")
|
||||||
public class StatsNamedContainerNullComputedValueTest {
|
public class StatsNamedContainerNullComputedValueTest {
|
||||||
|
|
||||||
|
private final static AtomicInteger invocationCounterNullProducer = new AtomicInteger();
|
||||||
|
private final static AtomicInteger invocationCounterValueProducer = new AtomicInteger();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNullComputedValue() {
|
public void testNullComputedValue() {
|
||||||
final StatsNamedContainer statsNamedContainer = new StatsNamedContainer<Integer>();
|
final StatsNamedContainer statsNamedContainer = new StatsNamedContainer<Integer>();
|
||||||
|
@ -27,4 +34,48 @@ public class StatsNamedContainerNullComputedValueTest {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
@Test
|
||||||
|
public void abletoStoreNullValues() {
|
||||||
|
final StatsNamedContainer statsNamedContainer = new StatsNamedContainer<Integer>();
|
||||||
|
Assert.assertEquals( 0, invocationCounterNullProducer.get() );
|
||||||
|
assertNull( getCacheWithNullValue( statsNamedContainer ) );
|
||||||
|
Assert.assertEquals( 1, invocationCounterNullProducer.get() );
|
||||||
|
assertNull( getCacheWithNullValue( statsNamedContainer ) );
|
||||||
|
Assert.assertEquals( 1, invocationCounterNullProducer.get() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void abletoStoreActualValues() {
|
||||||
|
final StatsNamedContainer statsNamedContainer = new StatsNamedContainer<Integer>();
|
||||||
|
Assert.assertEquals( 0, invocationCounterValueProducer.get() );
|
||||||
|
Assert.assertEquals( 5, getCacheWithActualValue( statsNamedContainer ) );
|
||||||
|
Assert.assertEquals( 1, invocationCounterValueProducer.get() );
|
||||||
|
Assert.assertEquals( 5, getCacheWithActualValue( statsNamedContainer ) );
|
||||||
|
Assert.assertEquals( 1, invocationCounterValueProducer.get() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getCacheWithActualValue(StatsNamedContainer statsNamedContainer) {
|
||||||
|
return statsNamedContainer.getOrCompute(
|
||||||
|
"key",
|
||||||
|
StatsNamedContainerNullComputedValueTest::produceValue
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getCacheWithNullValue(StatsNamedContainer statsNamedContainer) {
|
||||||
|
return statsNamedContainer.getOrCompute(
|
||||||
|
"key",
|
||||||
|
StatsNamedContainerNullComputedValueTest::produceNull
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer produceValue(Object o) {
|
||||||
|
invocationCounterValueProducer.getAndIncrement();
|
||||||
|
return Integer.valueOf( 5 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer produceNull(Object v) {
|
||||||
|
invocationCounterNullProducer.getAndIncrement();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue