blob: d9623df6db2b4c57b3a89ee370a9cea951d263d8 [file] [log] [blame]
package org.apache.directmemory.guava;
/*
* 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.
*/
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.apache.directmemory.cache.CacheService;
import com.google.common.base.Stopwatch;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheStats;
import com.google.common.cache.ForwardingCache;
import com.google.common.cache.RemovalCause;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import static com.google.common.cache.AbstractCache.SimpleStatsCounter;
import static com.google.common.cache.AbstractCache.StatsCounter;
public class OffHeapCache<K, V>
extends ForwardingCache.SimpleForwardingCache<K, V>
implements RemovalListener<K, V>
{
private final CacheService<K, V> cacheService;
private final StatsCounter statsCounter = new SimpleStatsCounter();
public OffHeapCache( CacheService<K, V> cacheService, Cache<K, V> primaryCache, ForwardingListener<K, V> listener )
{
super( primaryCache );
this.cacheService = cacheService;
listener.setDelegate( this );
}
@Override
public V getIfPresent( Object key )
{
V result = super.getIfPresent( key );
if ( result == null )
{
result = retrieve( key );
}
return result;
}
@Override
public V get( final K key, final Callable<? extends V> valueLoader )
throws ExecutionException
{
return super.get( key, new Callable<V>()
{
@Override
public V call()
throws Exception
{
//Check in offHeap first
V result = retrieve( key );
//Not found in L2 then load
if ( result == null )
{
result = valueLoader.call();
}
return result;
}
} );
}
@Override
public ImmutableMap<K, V> getAllPresent( Iterable<?> keys )
{
List<?> list = Lists.newArrayList( keys );
ImmutableMap<K, V> result = super.getAllPresent( list );
//All the requested keys found then no
//need to check L2
if ( result.size() == list.size() )
{
return result;
}
//Look up value from L2
Map<K, V> r2 = Maps.newHashMap( result );
for ( Object key : list )
{
if ( !result.containsKey( key ) )
{
V val = retrieve( key );
if ( val != null )
{
//Ideally the signature of method should have been
//getAllPresent(Iterable<? extends K> keys) in that
//case this cast would not have been required
r2.put( (K) key, val );
}
}
}
return ImmutableMap.copyOf( r2 );
}
@Override
public void invalidate( Object key )
{
super.invalidate( key );
cacheService.free( (K) key );
}
@Override
public void invalidateAll( Iterable<?> keys )
{
super.invalidateAll( keys );
for ( Object key : keys )
{
cacheService.free( (K) key );
}
}
/**
* it invokes clear on MemoryManagerService. If same
* MemoryManagerService is shared between multiple cacheService
* then it would lead to clearing of all other caches
*/
@Override
public void invalidateAll()
{
super.invalidateAll();
//TODO Problem with calling clear here is that
cacheService.clear();
}
@Override
public void onRemoval( RemovalNotification<K, V> notification )
{
if ( notification.getCause() == RemovalCause.SIZE )
{
cacheService.put( notification.getKey(), notification.getValue() );
}
}
public CacheStats offHeapStats()
{
return statsCounter.snapshot();
}
protected V retrieve( Object key )
{
Stopwatch watch = new Stopwatch().start();
V value = cacheService.retrieve( (K) key );
if ( value != null )
{
statsCounter.recordLoadSuccess( watch.elapsed( TimeUnit.NANOSECONDS ) );
}
else
{
statsCounter.recordMisses( 1 );
}
return value;
}
}