HBASE-1186 Memory-aware maps with LRU eviction for cell cache
git-svn-id: https://svn.apache.org/repos/asf/hadoop/hbase/trunk@758496 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f73bd2ce23
commit
dd3a140107
|
@ -123,7 +123,8 @@ Release 0.20.0 - Unreleased
|
||||||
(Nitay Joffe via Stack)
|
(Nitay Joffe via Stack)
|
||||||
HBASE-1285 Forcing compactions should be available via thrift
|
HBASE-1285 Forcing compactions should be available via thrift
|
||||||
(Tim Sell via Stack)
|
(Tim Sell via Stack)
|
||||||
|
HBASE-1186 Memory-aware Maps with LRU eviction for cell cache
|
||||||
|
(Jonathan Gray via Andrew Purtell)
|
||||||
|
|
||||||
Release 0.19.0 - 01/21/2009
|
Release 0.19.0 - 01/21/2009
|
||||||
INCOMPATIBLE CHANGES
|
INCOMPATIBLE CHANGES
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Copyright 2008 The Apache Software Foundation
|
* Copyright 2009 The Apache Software Foundation
|
||||||
*
|
*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,354 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2009 The Apache Software Foundation
|
||||||
|
*
|
||||||
|
* 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.regionserver;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
import org.apache.hadoop.hbase.HStoreKey;
|
||||||
|
|
||||||
|
public class TestLruHashMap extends TestCase {
|
||||||
|
private static LruHashMap<HStoreKey, HStoreKey> lru = null;
|
||||||
|
|
||||||
|
private static HStoreKey[] keys = null;
|
||||||
|
private static HStoreKey key = null;
|
||||||
|
private static HStoreKey tmpKey = null;
|
||||||
|
|
||||||
|
private static HStoreKey[] vals = null;
|
||||||
|
private static HStoreKey val = null;
|
||||||
|
private static HStoreKey tmpVal = null;
|
||||||
|
|
||||||
|
private static HStoreKey[] tmpData = null;
|
||||||
|
|
||||||
|
//Have to set type
|
||||||
|
private static Set<LruHashMap.Entry<HStoreKey, HStoreKey>> hashSet = null;
|
||||||
|
private static List<LruHashMap.Entry<HStoreKey, HStoreKey>> entryList = null;
|
||||||
|
private static LruHashMap.Entry<HStoreKey, HStoreKey> entry = null;
|
||||||
|
|
||||||
|
private static Random rand = null;
|
||||||
|
private static int ENTRY_ARRAY_LEN = 2000;
|
||||||
|
private static int LOOPS = 10;
|
||||||
|
|
||||||
|
protected void setUp()
|
||||||
|
throws Exception{
|
||||||
|
super.setUp();
|
||||||
|
long maxMemUsage = 10000000L;
|
||||||
|
//Using the default values for everything, except memUsage
|
||||||
|
lru = new LruHashMap<HStoreKey, HStoreKey>(maxMemUsage);
|
||||||
|
|
||||||
|
|
||||||
|
rand = new Random();
|
||||||
|
|
||||||
|
keys = new HStoreKey[ENTRY_ARRAY_LEN];
|
||||||
|
vals = new HStoreKey[ENTRY_ARRAY_LEN];
|
||||||
|
tmpData = new HStoreKey[ENTRY_ARRAY_LEN];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void tearDown()
|
||||||
|
throws Exception{
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test adds data to the Lru and checks that the head and tail pointers
|
||||||
|
* are updated correctly
|
||||||
|
*/
|
||||||
|
public void testAdd_Pointers(){
|
||||||
|
for(int i=0; i<LOOPS; i++){
|
||||||
|
sequential(keys);
|
||||||
|
tmpKey = keys[0];
|
||||||
|
|
||||||
|
for(HStoreKey key: keys){
|
||||||
|
lru.put(key, key);
|
||||||
|
assertTrue("headPtr key not correct",
|
||||||
|
lru.getHeadPtr().getKey().equals(tmpKey));
|
||||||
|
|
||||||
|
assertTrue("tailPtr key not correct",
|
||||||
|
lru.getTailPtr().getKey().equals(key));
|
||||||
|
}
|
||||||
|
lru.clear();
|
||||||
|
}
|
||||||
|
System.out.println("testAdd_Pointers: OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test adds data to the Lru and checks that the memFree variable never
|
||||||
|
* goes below 0
|
||||||
|
*/
|
||||||
|
public void testAdd_MemUsage_random(){
|
||||||
|
for(int i=0; i<LOOPS; i++){
|
||||||
|
random(keys);
|
||||||
|
|
||||||
|
for(HStoreKey key : keys){
|
||||||
|
lru.put(key, key);
|
||||||
|
|
||||||
|
assertTrue("Memory usage exceeded!", lru.getMemFree() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
lru.clear();
|
||||||
|
}
|
||||||
|
System.out.println("testAdd_MemUsage: OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test adds data to the Lru and checks that the memFree variable never
|
||||||
|
* goes below 0
|
||||||
|
*/
|
||||||
|
public void testAdd_MemUsage_sequential(){
|
||||||
|
for(int i=0; i<LOOPS; i++){
|
||||||
|
sequential(keys);
|
||||||
|
|
||||||
|
for(HStoreKey key : keys){
|
||||||
|
lru.put(key, key);
|
||||||
|
|
||||||
|
assertTrue("Memory usage exceeded!", lru.getMemFree() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
lru.clear();
|
||||||
|
}
|
||||||
|
System.out.println("testAdd_MemUsage: OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test adds data to the Lru and checks that the order in the lru is the
|
||||||
|
* same as the insert order
|
||||||
|
*/
|
||||||
|
public void testAdd_Order()
|
||||||
|
throws Exception{
|
||||||
|
for(int i=0; i<LOOPS; i++){
|
||||||
|
//Adding to Lru
|
||||||
|
put();
|
||||||
|
|
||||||
|
//Getting order from lru
|
||||||
|
entryList = lru.entryLruList();
|
||||||
|
|
||||||
|
//Comparing orders
|
||||||
|
assertTrue("Different lengths" , keys.length == entryList.size());
|
||||||
|
int j = 0;
|
||||||
|
for(Map.Entry entry : entryList){
|
||||||
|
//Comparing keys
|
||||||
|
assertTrue("Different order", keys[j++].equals(entry.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Clearing the Lru
|
||||||
|
lru.clear();
|
||||||
|
}
|
||||||
|
System.out.println("testAdd_Order: OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test adds data to the Lru, clears it and checks that the memoryUsage
|
||||||
|
* looks ok afterwards
|
||||||
|
*/
|
||||||
|
public void testAdd_Clear()
|
||||||
|
throws Exception{
|
||||||
|
long initMemUsage = 0L;
|
||||||
|
long putMemUsage = 0L;
|
||||||
|
long clearMemUsage = 0L;
|
||||||
|
for(int i=0; i<LOOPS; i++){
|
||||||
|
initMemUsage = lru.getMemFree();
|
||||||
|
|
||||||
|
//Adding to Lru
|
||||||
|
put();
|
||||||
|
putMemUsage = lru.getMemFree();
|
||||||
|
|
||||||
|
//Clearing the Lru
|
||||||
|
lru.clear();
|
||||||
|
clearMemUsage = lru.getMemFree();
|
||||||
|
assertTrue("memUsage went down", clearMemUsage <= initMemUsage);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("testAdd_Clear: OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test adds data to the Lru and checks that all the data that is in
|
||||||
|
* the hashSet is also in the EntryList
|
||||||
|
*/
|
||||||
|
public void testAdd_Containment(){
|
||||||
|
for(int i=0; i<LOOPS; i++){
|
||||||
|
//Adding to Lru
|
||||||
|
put();
|
||||||
|
|
||||||
|
//Getting HashSet
|
||||||
|
hashSet = lru.entryTableSet();
|
||||||
|
|
||||||
|
//Getting EntryList
|
||||||
|
entryList = lru.entryLruList();
|
||||||
|
|
||||||
|
//Comparing
|
||||||
|
assertTrue("Wrong size", hashSet.size() == entryList.size());
|
||||||
|
for(int j=0; j<entryList.size(); j++){
|
||||||
|
assertTrue("Set doesn't contain value from list",
|
||||||
|
hashSet.contains(entryList.get(j)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Clearing the Lru
|
||||||
|
lru.clear();
|
||||||
|
}
|
||||||
|
System.out.println("testAdd_Containment: OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test gets an entry from the map and checks that the position of it has
|
||||||
|
* been updated afterwards.
|
||||||
|
*/
|
||||||
|
public void testGet(){
|
||||||
|
int getter = 0;
|
||||||
|
|
||||||
|
for(int i=0; i<LOOPS; i++){
|
||||||
|
//Adding to Lru
|
||||||
|
put();
|
||||||
|
|
||||||
|
//Getting a random entry from the map
|
||||||
|
getter = rand.nextInt(ENTRY_ARRAY_LEN);
|
||||||
|
key = keys[getter];
|
||||||
|
val = lru.get(key);
|
||||||
|
|
||||||
|
//Checking if the entries position has changed
|
||||||
|
entryList = lru.entryLruList();
|
||||||
|
tmpKey = entryList.get(entryList.size()-1).getKey();
|
||||||
|
assertTrue("Get did not put entry first", tmpKey.equals(key));
|
||||||
|
|
||||||
|
if(getter != ENTRY_ARRAY_LEN -1){
|
||||||
|
tmpKey = entryList.get(getter).getKey();
|
||||||
|
assertFalse("Get did leave entry in same position", tmpKey.equals(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
lru.clear();
|
||||||
|
}
|
||||||
|
System.out.println("testGet: OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an entry in the map and checks that the position of it has been
|
||||||
|
* updated afterwards.
|
||||||
|
*/
|
||||||
|
public void testUpdate(){
|
||||||
|
for(int i=0; i<LOOPS; i++){
|
||||||
|
//Adding to Lru
|
||||||
|
put();
|
||||||
|
|
||||||
|
//Getting a random entry from the map
|
||||||
|
key = keys[rand.nextInt(ENTRY_ARRAY_LEN)];
|
||||||
|
val = random(val);
|
||||||
|
|
||||||
|
tmpVal = lru.put(key, val);
|
||||||
|
|
||||||
|
//Checking if the value has been updated and that the position i first
|
||||||
|
entryList = lru.entryLruList();
|
||||||
|
tmpKey = entryList.get(entryList.size()-1).getKey();
|
||||||
|
assertTrue("put(update) did not put entry first", tmpKey.equals(key));
|
||||||
|
if(!val.equals(tmpVal)){
|
||||||
|
assertTrue("Value was not updated",
|
||||||
|
entryList.get(entryList.size()-1).getValue().equals(val));
|
||||||
|
assertFalse("Value was not updated",
|
||||||
|
entryList.get(entryList.size()-1).getValue().equals(tmpVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
lru.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("testUpdate: OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an entry in the map and checks that it is no longer in the
|
||||||
|
* entryList nor the HashSet afterwards
|
||||||
|
*/
|
||||||
|
public void testRemove(){
|
||||||
|
for(int i=0; i<LOOPS; i++){
|
||||||
|
//Adding to Lru
|
||||||
|
put();
|
||||||
|
entryList = lru.entryLruList();
|
||||||
|
|
||||||
|
//Getting a random entry from the map
|
||||||
|
key = keys[rand.nextInt(ENTRY_ARRAY_LEN)];
|
||||||
|
val = lru.remove(key);
|
||||||
|
|
||||||
|
//Checking key is in list
|
||||||
|
entryList = lru.entryLruList();
|
||||||
|
for(int j=0; j<entryList.size(); j++){
|
||||||
|
assertFalse("Entry found in list after remove",
|
||||||
|
entryList.get(j).equals(key));
|
||||||
|
}
|
||||||
|
lru.clear();
|
||||||
|
}
|
||||||
|
System.out.println("testRemove: OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Helpers
|
||||||
|
private static void put(){
|
||||||
|
//Setting up keys and values
|
||||||
|
random(keys);
|
||||||
|
vals = keys;
|
||||||
|
|
||||||
|
//Inserting into Lru
|
||||||
|
for(int i=0; i<keys.length; i++){
|
||||||
|
lru.put(keys[i], vals[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generating data
|
||||||
|
private static HStoreKey random(HStoreKey data){
|
||||||
|
return new HStoreKey(Bytes.toBytes(rand.nextInt(ENTRY_ARRAY_LEN)));
|
||||||
|
}
|
||||||
|
private static void random(HStoreKey[] keys){
|
||||||
|
final int LENGTH = keys.length;
|
||||||
|
Set<Integer> set = new HashSet<Integer>();
|
||||||
|
for(int i=0; i<LENGTH; i++){
|
||||||
|
Integer pos = 0;
|
||||||
|
while(set.contains(pos = new Integer(rand.nextInt(LENGTH)))){}
|
||||||
|
set.add(pos);
|
||||||
|
keys[i] = new HStoreKey(Bytes.toBytes(pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sequential(HStoreKey[] keys){
|
||||||
|
for(int i=0; i<keys.length; i++){
|
||||||
|
keys[i] = new HStoreKey(Bytes.toBytes(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//testAdd
|
||||||
|
private HStoreKey[] mapEntriesToArray(List<LruHashMap.Entry<HStoreKey,
|
||||||
|
HStoreKey>> entryList){
|
||||||
|
List<HStoreKey> res = new ArrayList<HStoreKey>();
|
||||||
|
for(Map.Entry<HStoreKey, HStoreKey> entry : entryList){
|
||||||
|
res.add(entry.getKey());
|
||||||
|
}
|
||||||
|
return res.toArray(new HStoreKey[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue