package com.ctriposs.sdb.merge;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import org.junit.Test;
import org.xerial.snappy.Snappy;
import com.ctriposs.sdb.LevelQueue;
import com.ctriposs.sdb.SDB;
import com.ctriposs.sdb.merge.Level0Merger;
import com.ctriposs.sdb.table.AbstractMapTable;
import com.ctriposs.sdb.table.GetResult;
import com.ctriposs.sdb.table.HashMapTable;
import com.ctriposs.sdb.table.IMapEntry;
import com.ctriposs.sdb.table.MMFMapTable;
import com.ctriposs.sdb.utils.TestUtil;
public class Level0MergerTest {
private static String testDir = TestUtil.TEST_BASE_DIR + "sdb/unit/level_0_merger_test";
@Test
public void testCase02() throws IOException, ClassNotFoundException {
HashMapTable[] sourceTables = new HashMapTable[4];
LevelQueue lq0 = new LevelQueue();
for(int i = 0; i < 4; i++) {
sourceTables[i] = new HashMapTable(testDir, SDB.LEVEL0, System.nanoTime() + i);
sourceTables[i].setCompressionEnabled(true);
lq0.addFirst(sourceTables[i]);
assertTrue(sourceTables[i].isImmutable());
}
// delete
int max = AbstractMapTable.INIT_INDEX_ITEMS_PER_TABLE;
HashMapTable table4 = sourceTables[3];
int start = 0;
while(start < max) {
table4.delete(String.valueOf(start).getBytes());
start = start + 4;
}
// expiration
HashMapTable table3 = sourceTables[2];
start = 1;
while(start < max) {
table3.put(String.valueOf(start).getBytes(), String.valueOf(start).getBytes(), 200, System.currentTimeMillis());
start = start + 4;
}
// expire table3
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// ignore
}
// time to live 60 sec
HashMapTable table2 = sourceTables[1];
start = 2;
while(start < max) {
table2.put(String.valueOf(start).getBytes(), String.valueOf(start).getBytes(), 60 * 1000, System.currentTimeMillis());
start = start + 4;
}
// time to live 120 sec with items that have been updated(table 2), deleted(table 4) and expired(table 3)
HashMapTable table1 = sourceTables[0];
start = 0;
while(start < max) {
table1.put(String.valueOf(start).getBytes(), String.valueOf(start).getBytes(), 120 * 1000, System.currentTimeMillis());
start = start + 1;
}
LevelQueue lq1 = new LevelQueue();
Level0Merger.mergeSort(lq0, lq1, 4, testDir, (short)0);
for(int i = 0; i < 4; i++) {
assertTrue(sourceTables[i].isImmutable());
}
assertTrue(lq1.size() == 1);
MMFMapTable targetTable = (MMFMapTable) lq1.poll();
assertTrue(targetTable.getLevel() == SDB.LEVEL1);
assertTrue(targetTable.getAppendedSize() == max);
// validate delete
start = 0;
while(start < max) {
GetResult result = targetTable.get(String.valueOf(start).getBytes());
assertTrue(result.isFound());
assertTrue(result.isDeleted());
start += 4;
}
// validate expiration
start = 1;
while(start < max) {
GetResult result = targetTable.get(String.valueOf(start).getBytes());
assertTrue(result.isFound());
assertTrue(result.isExpired());
start += 4;
}
// validate ttl 60s
start = 2;
while(start < max) {
GetResult result = targetTable.get(String.valueOf(start).getBytes());
assertTrue(result.isFound());
assertTrue(result.getTimeToLive() == 60 * 1000);
start += 4;
}
// validate ttl 120s
start = 3;
while(start < max) {
GetResult result = targetTable.get(String.valueOf(start).getBytes());
assertTrue(result.isFound());
assertTrue(result.getTimeToLive() == 120 * 1000);
start += 4;
}
List<String> keyList = new ArrayList<String>();
for(long i = 0; i < max; i++) {
keyList.add(String.valueOf(i));
}
Collections.sort(keyList, new Comparator<String>() {
@Override
public int compare(String arg0, String arg1) {
int hash0 = Arrays.hashCode(arg0.getBytes());
int hash1 = Arrays.hashCode(arg1.getBytes());
if (hash0 < hash1) return -1;
else if (hash0 > hash1) return 1;
else return 0;
}
});
for(int i = 0; i < max; i++) {
IMapEntry mapEntry = targetTable.getMapEntry(i);
assertTrue(mapEntry.getIndex() == i);
assertTrue(new String(mapEntry.getKey()).equals(keyList.get(i)));
int intKey = Integer.parseInt(new String(mapEntry.getKey()));
// validate disk space optimization
if (intKey % 4 == 0) // deleted
assertTrue(Arrays.equals(new byte[] {0}, mapEntry.getValue()));
else if (intKey % 4 == 1) // expired
assertTrue(Arrays.equals(new byte[] {0}, mapEntry.getValue()));
else {
assertTrue(new String(Snappy.uncompress(mapEntry.getValue())).equals(keyList.get(i)));
//assertTrue(new String(mapEntry.getValue()).equals(keyList.get(i)));
}
}
Random random = new Random();
for(int i = 0; i < 1024; i++) {
long key = random.nextInt(max);
GetResult result = targetTable.get(String.valueOf(key).getBytes());
assertTrue(result.isFound());
}
targetTable.close();
targetTable.delete();
}
@Test
public void testCase01() throws IOException, ClassNotFoundException {
String value = TestUtil.randomString(128);
HashMapTable[] sourceTables = new HashMapTable[4];
LevelQueue lq0 = new LevelQueue();
for(int i = 0; i < 4; i++) {
sourceTables[i] = new HashMapTable(testDir, SDB.LEVEL0, System.nanoTime() + i);
sourceTables[i].setCompressionEnabled(true);
lq0.addFirst(sourceTables[i]);
assertTrue(sourceTables[i].isImmutable());
}
int totalCount = 0;
int max = 0;
for(int i = 0; i < 4; i++) {
int start = i;
HashMapTable table = sourceTables[i];
while(table.put(String.valueOf(start).getBytes(), value.getBytes(), AbstractMapTable.NO_TIMEOUT, System.currentTimeMillis(), false)) {
totalCount++;
if (start > max) max = start;
start = start + 4;
}
}
LevelQueue lq1 = new LevelQueue();
long start = System.currentTimeMillis();
Level0Merger.mergeSort(lq0, lq1, 4, testDir, (short)1);
long end = System.currentTimeMillis();
System.out.println("Time spent to merge " + totalCount + " items in 4 ways is " + (end - start) / 1000 + "s");
for(int i = 0; i < 4; i++) {
assertTrue(sourceTables[i].isImmutable());
}
assertTrue(lq1.size() == 1);
MMFMapTable targetTable = (MMFMapTable) lq1.poll();
assertTrue(targetTable.getLevel() == SDB.LEVEL1);
assertTrue(targetTable.getAppendedSize() == totalCount);
List<String> keyList = new ArrayList<String>();
for(long i = 0; i < totalCount; i++) {
keyList.add(String.valueOf(i));
}
Collections.sort(keyList, new Comparator<String>() {
@Override
public int compare(String arg0, String arg1) {
int hash0 = Arrays.hashCode(arg0.getBytes());
int hash1 = Arrays.hashCode(arg1.getBytes());
if (hash0 < hash1) return -1;
else if (hash0 > hash1) return 1;
else return 0;
}
});
for(int i = 0; i < totalCount; i++) {
IMapEntry mapEntry = targetTable.getMapEntry(i);
assertTrue(mapEntry.getIndex() == i);
assertTrue(new String(mapEntry.getKey()).equals(keyList.get(i)));
assertTrue(new String(Snappy.uncompress(mapEntry.getValue())).equals(value));
}
start = System.currentTimeMillis();
Random random = new Random();
for(int i = 0; i < 1024; i++) {
long key = random.nextInt(totalCount);
GetResult result = targetTable.get(String.valueOf(key).getBytes());
assertTrue(result.isFound());
}
end = System.currentTimeMillis();
System.out.println("Time to lookup 1024 random key in the target table is " + (end - start) + "ms");
targetTable.close();
targetTable.delete();
}
}