Package com.cetsoft.imcache.cache.offheap

Source Code of com.cetsoft.imcache.cache.offheap.OffHeapCache

/*
* Copyright (C) 2013 Cetsoft, http://www.cetsoft.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Author : Yusuf Aytas
* Date   : Sep 20, 2013
*/
package com.cetsoft.imcache.cache.offheap;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import com.cetsoft.imcache.bytebuffer.OffHeapByteBuffer;
import com.cetsoft.imcache.bytebuffer.OffHeapByteBufferStore;
import com.cetsoft.imcache.bytebuffer.Pointer;
import com.cetsoft.imcache.cache.AbstractCache;
import com.cetsoft.imcache.cache.CacheLoader;
import com.cetsoft.imcache.cache.EvictionListener;
import com.cetsoft.imcache.cache.search.IndexHandler;
import com.cetsoft.imcache.concurrent.lock.StripedReadWriteLock;
import com.cetsoft.imcache.serialization.Serializer;

/**
* The Class OffHeapCache is a cache that uses offheap byte buffers
* to store or retrieve data by serializing items into bytes. To do so,
* OffHeapCache uses pointers to point array location of an item.
* OffHeapCache clears the buffers periodically to gain free space if
* buffers are dirty(unused memory). It also does eviction depending on
* access time to the objects.
*
* @param <K> the key type
* @param <V> the value type
*/
public class OffHeapCache<K, V> extends AbstractCache<K, V> {

  /** The Constant DELTA. */
  private static final float DELTA = 0.00001f;
 
  /** The default buffer cleaner period which is 10 minutes. */
  public static final long DEFAULT_BUFFER_CLEANER_PERIOD = 10 * 60 * 1000;
 
  /** The default eviction period which is 10 minutes. */
  public static final long DEFAULT_EVICTION_PERIOD = 10 * 60 * 1000;

  /** The default buffer cleaner threshold. */
  public static final float DEFAULT_BUFFER_CLEANER_THRESHOLD = 0.5f;
 
  /** The Constant DEFAULT_CONCURRENCY_LEVEL. */
  public static final int DEFAULT_CONCURRENCY_LEVEL = 4;

  /** The hit. */
  protected AtomicLong hit = new AtomicLong();

  /** The miss. */
  protected AtomicLong miss = new AtomicLong();

  /** The pointer map. */
  private ConcurrentMap<K, Pointer> pointerMap = new ConcurrentHashMap<K, Pointer>();

  /** The serializer. */
  private Serializer<V> serializer;

  /** The buffer store. */
  private OffHeapByteBufferStore bufferStore;

  /** The read write lock. */
  private StripedReadWriteLock readWriteLock;

  /** The Constant NO_OF_CLEANERS. */
  private static final AtomicInteger NO_OF_CLEANERS = new AtomicInteger();
 
  /** The Constant NO_OF_EVICTORS. */
  private static final AtomicInteger NO_OF_EVICTORS = new AtomicInteger();
 
  /**
   * Instantiates a new offheap cache.
   *
   * @param cacheLoader the cache loader
   * @param evictionListener the eviction listener
   * @param indexHandler the query executer
   * @param byteBufferStore the byte buffer store
   * @param serializer the serializer
   * @param bufferCleanerPeriod the buffer cleaner period
   * @param bufferCleanerThreshold the buffer cleaner threshold
   * @param concurrencyLevel the concurrency level
   * @param evictionPeriod the eviction period
   */
  public OffHeapCache(CacheLoader<K, V> cacheLoader, EvictionListener<K, V> evictionListener,IndexHandler<K, V> indexHandler,
      OffHeapByteBufferStore byteBufferStore, Serializer<V> serializer,long bufferCleanerPeriod,
      final float bufferCleanerThreshold, int concurrencyLevel, final long evictionPeriod) {
    super(cacheLoader,evictionListener,indexHandler);
    initCache(byteBufferStore, serializer, bufferCleanerPeriod, bufferCleanerThreshold, concurrencyLevel, evictionPeriod);
  }
 
  /**
   * Inits the cache.
   *
   * @param byteBufferStore the byte buffer store
   * @param serializer the serializer
   * @param bufferCleanerPeriod the buffer cleaner period
   * @param bufferCleanerThreshold the buffer cleaner threshol
   * @param concurrencyLevel the concurrency level
   * @param evictionPeriod the eviction period
   */
  protected void initCache(OffHeapByteBufferStore byteBufferStore, Serializer<V> serializer,
      long bufferCleanerPeriod, final float bufferCleanerThreshold, int concurrencyLevel, final long evictionPeriod) {
    if(concurrencyLevel>11||concurrencyLevel<0){
      throw new IllegalArgumentException("ConcurrencyLevel must be between 0 and 11 inclusive!");
    }
    this.serializer = serializer;
    this.bufferStore = byteBufferStore;
    this.readWriteLock = new StripedReadWriteLock(concurrencyLevel);
    ScheduledExecutorService cleanerService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
      public Thread newThread(Runnable runnable) {
        return new Thread(runnable, "imcache:bufferCleaner("+ NO_OF_CLEANERS.incrementAndGet() + ")");
      }
    });
    cleanerService.scheduleAtFixedRate(new Runnable() {
      public void run() {
        Collection<Pointer> pointers = pointerMap.values();
        List<Pointer> pointersToBeRedistributed = new ArrayList<Pointer>();
        Map<OffHeapByteBuffer, Float> buffers = new HashMap<OffHeapByteBuffer, Float>();
        Set<Integer> buffersToBeCleaned = new HashSet<Integer>();
        for (Pointer pointer : pointers) {
          Float ratio = buffers.get(pointer.getOffHeapByteBuffer());
          if (ratio == null) {
            ratio = (float) (pointer.getOffHeapByteBuffer().dirtyMemory() / (pointer.getOffHeapByteBuffer().freeMemory()
                + pointer.getOffHeapByteBuffer().usedMemory() + pointer.getOffHeapByteBuffer().dirtyMemory()));
            buffers.put(pointer.getOffHeapByteBuffer(), ratio);
          }
          if (ratio - bufferCleanerThreshold > DELTA) {
            pointersToBeRedistributed.add(pointer);
            buffersToBeCleaned.add(pointer.getOffHeapByteBuffer().getIndex());
          }
        }
        bufferStore.redistribute(pointersToBeRedistributed);
        for (Integer bufferIndex : buffersToBeCleaned) {
          bufferStore.free(bufferIndex);
        }
      }

    }, 0, bufferCleanerPeriod, TimeUnit.MILLISECONDS);
    ScheduledExecutorService evictionService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
      public Thread newThread(Runnable runnable) {
        return new Thread(runnable, "imcache:evictor("+ NO_OF_EVICTORS.incrementAndGet() + ")");
      }
    });
    evictionService.scheduleAtFixedRate(new Runnable() {
      public void run() {
        Set<Entry<K,Pointer>> entries = pointerMap.entrySet();
        for (Entry<K,Pointer> entry : entries) {
          synchronized (entry.getValue()) {
            if(entry.getValue().getAccessTime()+evictionPeriod<System.currentTimeMillis()){
              invalidate(entry.getKey());
            }
          }
        }
      }
    }, 0, evictionPeriod, TimeUnit.MILLISECONDS);
  }

  /*
   * (non-Javadoc)
   *
   * @see com.cetsoft.imcache.cache.Cache#put(java.lang.Object, java.lang.Object)
   */
  public void put(K key, V value) {
    writeLock(key);
    Pointer pointer = pointerMap.get(key);
    try {
      if (pointer == null) {
        pointer = bufferStore.store(serializer.serialize(value));
      } else {
        synchronized (pointer) {
          pointer = bufferStore.update(pointer, serializer.serialize(value));
        }
      }
      pointerMap.put(key, pointer);
    } finally {
      writeUnlock(key);
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.cetsoft.imcache.cache.Cache#get(java.lang.Object)
   */
  public V get(K key) {
    Pointer pointer = pointerMap.get(key);
    if (pointer != null) {
      readLock(key);
      try {
        hit.incrementAndGet();
        synchronized (pointer) {
          byte[] payload = bufferStore.retrieve(pointer);
          return serializer.deserialize(payload);
        }
      } finally {
        readUnlock(key);
      }
    } else {
      miss.incrementAndGet();
      V value = cacheLoader.load(key);
      if (value != null) {
        put(key, value);
      }
      return value;
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.cetsoft.imcache.cache.Cache#invalidate(java.lang.Object)
   */
  public V invalidate(K key) {
    writeLock(key);
    try{
      Pointer pointer = pointerMap.get(key);
      if (pointer != null) {
        synchronized (pointer) {
          byte[] payload = bufferStore.remove(pointer);
          return serializer.deserialize(payload);
        }
      }
    }finally{
      writeUnlock(key);
    }
    return null;
  }

  /*
   * (non-Javadoc)
   *
   * @see com.cetsoft.imcache.cache.Cache#contains(java.lang.Object)
   */
  public boolean contains(K key) {
    return pointerMap.containsKey(key);
  }

  /*
   * (non-Javadoc)
   *
   * @see com.cetsoft.imcache.cache.Cache#clear()
   */
  public void clear() {
    pointerMap.clear();
    bufferStore.free();
  }

  /*
   * (non-Javadoc)
   *
   * @see com.cetsoft.imcache.cache.Cache#hitRatio()
   */
  public double hitRatio() {
    return hit.get() / (hit.get() + miss.get());
  }

  /**
   * Read Lock for key is locked.
   *
   * @param key the key
   */
  protected void readLock(K key) {
    readWriteLock.readLock(Math.abs(key.hashCode()));
  }

  /**
   * Read Lock for key is unlocked.
   *
   * @param key the key
   */
  protected void readUnlock(K key) {
    readWriteLock.readUnlock(Math.abs(key.hashCode()));
  }

  /**
   * Write Lock for key is locked..
   *
   * @param key the key
   */
  protected void writeLock(K key) {
    readWriteLock.writeLock(Math.abs(key.hashCode()));
  }

  /**
   * Write Lock for key is unlocked.
   *
   * @param key the key
   */
  protected void writeUnlock(K key) {
    readWriteLock.writeUnlock(Math.abs(key.hashCode()));
  }
 
}
TOP

Related Classes of com.cetsoft.imcache.cache.offheap.OffHeapCache

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.