/**
* Copyright 2011-2012 Akiban Technologies, Inc.
*
* Licensed 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 com.persistit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Random;
import org.junit.Test;
import com.persistit.Exchange.Sequence;
import com.persistit.policy.SplitPolicy;
public class SplitPolicyTest extends PersistitUnitTestCase {
@Test
public void testLeftBias() {
final Buffer nullBuffer = null;
final int mockLeftSize = 20;
int capacity = 0;
final SplitPolicy leftBias = SplitPolicy.LEFT_BIAS;
assertEquals("LEFT", leftBias.toString());
int measure = leftBias.splitFit(nullBuffer, 0, 0, false, mockLeftSize, 0, 0, 0, capacity, 0, Sequence.NONE);
/* splitFit should return 0 since leftSize is larger than capcity */
assertEquals(0, measure);
capacity = 21;
measure = leftBias.splitFit(nullBuffer, 0, 0, false, mockLeftSize, 0, 0, 0, capacity, 0, Sequence.NONE);
/* splitFit just returns the given leftSize for LEFT_BIAS policy */
assertEquals(mockLeftSize, measure);
}
@Test
public void testRightBias() {
final Buffer nullBuffer = null;
final int mockRightSize = 20;
int capacity = 0;
final SplitPolicy rightBias = SplitPolicy.RIGHT_BIAS;
assertEquals("RIGHT", rightBias.toString());
int measure = rightBias.splitFit(nullBuffer, 0, 0, false, 0, mockRightSize, 0, 0, capacity, 0, Sequence.NONE);
/* splitFit should return 0 since rightSize is larger than capacity */
assertEquals(0, measure);
capacity = 21;
measure = rightBias.splitFit(nullBuffer, 0, 0, false, 0, mockRightSize, 0, 0, capacity, 0, Sequence.NONE);
/* splitFit just returns the given rightSize for RIGHT_BIAS policy */
assertEquals(mockRightSize, measure);
}
@Test
public void testEvenBias() {
final Buffer nullBuffer = null;
int mockRightSize = 20;
int mockLeftSize = 20;
int capacity = 0;
final SplitPolicy evenBias = SplitPolicy.EVEN_BIAS;
assertEquals("EVEN", evenBias.toString());
int measure = evenBias.splitFit(nullBuffer, 0, 0, false, mockLeftSize, mockRightSize, 0, 0, capacity, 0,
Sequence.NONE);
/*
* splitFit should return 0 since rightSize & leftSize are larger than
* capacity
*/
assertEquals(0, measure);
capacity = 21;
measure = evenBias.splitFit(nullBuffer, 0, 0, false, mockLeftSize, mockRightSize, 0, 0, capacity, 0,
Sequence.NONE);
/*
* splitFit returns (capacity - abs(rightSize - leftSize)) for EVEN_BIAS
* policy
*/
assertEquals(capacity, measure);
capacity = 21;
mockLeftSize = 5;
mockRightSize = 15;
measure = evenBias.splitFit(nullBuffer, 0, 0, false, mockLeftSize, mockRightSize, 0, 0, capacity, 0,
Sequence.NONE);
/*
* splitFit returns (capacity - abs(rightSize - leftSize)) for EVEN_BIAS
* policy
*/
assertEquals(11, measure);
}
@Test
public void testNiceBias() {
final Buffer nullBuffer = null;
int mockRightSize = 20;
int mockLeftSize = 20;
int capacity = 0;
final SplitPolicy niceBias = SplitPolicy.NICE_BIAS;
assertEquals("NICE", niceBias.toString());
int measure = niceBias.splitFit(nullBuffer, 0, 0, false, mockLeftSize, mockRightSize, 0, 0, capacity, 0,
Sequence.NONE);
/*
* splitFit should return 0 since rightSize & leftSize are larger than
* capacity
*/
assertEquals(0, measure);
capacity = 21;
measure = niceBias.splitFit(nullBuffer, 0, 0, false, mockLeftSize, mockRightSize, 0, 0, capacity, 0,
Sequence.NONE);
/*
* splitFit returns ((capacity * 2) - abs((2 * rightSize) - leftSize))
* for EVEN_BIAS policy
*/
assertEquals(22, measure);
capacity = 21;
mockLeftSize = 5;
mockRightSize = 15;
measure = niceBias.splitFit(nullBuffer, 0, 0, false, mockLeftSize, mockRightSize, 0, 0, capacity, 0,
Sequence.NONE);
/*
* splitFit returns ((capacity * 2) - abs((2 * rightSize) - leftSize))
* for EVEN_BIAS policy
*/
assertEquals(17, measure);
}
@Test
public void testPackBias() throws Exception {
final Exchange ex = _persistit.getExchange("persistit", "SplitPolicyTest", true);
ex.getValue().put("aaabbbcccdddeee");
ex.to(1);
final long page = ex.fetchBufferCopy(0).getPageAddress();
final Buffer buffer = ex.getBufferPool().get(ex.getVolume(), page, false, true);
buffer.releaseTouched();
for (int i = 0; buffer.getAvailableSize() > 100; i++) {
ex.to(i).store();
}
int mockRightSize = 20;
int mockLeftSize = 20;
int capacity = 0;
final SplitPolicy packBias = SplitPolicy.PACK_BIAS;
assertEquals("PACK", packBias.toString());
//
// For non-sequential inserts, works the same as NICE_BIAS.
//
int measure = packBias.splitFit(buffer, 0, 0, false, mockLeftSize, mockRightSize, 0, 0, capacity, 0,
Sequence.NONE);
/*
* splitFit should return 0 since rightSize & leftSize are larger than
* capacity
*/
assertEquals(0, measure);
capacity = 21;
measure = packBias.splitFit(buffer, 0, 0, false, mockLeftSize, mockRightSize, 0, 0, capacity, 0, Sequence.NONE);
/*
* splitFit returns ((capacity * 2) - abs((2 * rightSize) - leftSize))
* for EVEN_BIAS policy
*/
assertEquals(22, measure);
capacity = 21;
mockLeftSize = 5;
mockRightSize = 15;
measure = packBias.splitFit(buffer, 0, 0, false, mockLeftSize, mockRightSize, 0, 0, capacity, 0, Sequence.NONE);
/*
* splitFit returns ((capacity * 2) - abs((2 * rightSize) - leftSize))
* for EVEN_BIAS policy
*/
assertEquals(17, measure);
//
// Sequential insert cases
//
for (int p = buffer.getKeyBlockStart(); p < buffer.getKeyBlockEnd(); p += Buffer.KEYBLOCK_LENGTH) {
final int splitBest = split(packBias, buffer, p, Sequence.FORWARD);
if (p > buffer.getKeyBlockStart() + 256 && p < buffer.getKeyBlockEnd() - 256) {
assertEquals(splitBest, p);
}
}
for (int p = buffer.getKeyBlockStart(); p < buffer.getKeyBlockEnd(); p += Buffer.KEYBLOCK_LENGTH) {
final int splitBest = split(packBias, buffer, p, Sequence.REVERSE);
if (p > buffer.getKeyBlockStart() + 260 && p < buffer.getKeyBlockEnd() - 260) {
assertEquals(splitBest, p + Buffer.KEYBLOCK_LENGTH);
}
}
}
private int split(final SplitPolicy policy, final Buffer buffer, final int foundAt, final Sequence sequence) {
int best = -1;
int bestMeasure = -1;
int leftSize = 0;
int rightSize = buffer.getBufferSize() + 10;
final int perKeySize = rightSize / buffer.getKeyCount();
for (int p = buffer.getKeyBlockStart(); p < buffer.getKeyBlockEnd(); p += Buffer.KEYBLOCK_LENGTH) {
final int measure = policy.splitFit(buffer, p, foundAt, false, leftSize, rightSize,
buffer.getBufferSize() - 100, leftSize + rightSize, buffer.getBufferSize(), best, sequence);
if (measure > bestMeasure) {
best = p;
bestMeasure = measure;
}
leftSize += perKeySize;
rightSize -= perKeySize;
}
return best;
}
@Test
public void testPackBiasPacking() throws Exception {
final Exchange ex = _persistit.getExchange("persistit", "SplitPolicyTest", true);
final Random random = new Random(1);
ex.setSplitPolicy(SplitPolicy.PACK_BIAS);
ex.getValue().put("aaabbbcccdddeee");
ex.removeAll();
for (int i = 0; ex.getVolume().getStorage().getNextAvailablePage() < 20; i++) {
ex.to(i).store();
}
final float ratioFowardSequential = inuseRatio(ex);
assertTrue(ratioFowardSequential > .85);
ex.removeAll();
for (int i = 1000000; ex.getVolume().getStorage().getNextAvailablePage() < 21; i--) {
ex.to(i).store();
}
final float ratioReverseSequential = inuseRatio(ex);
assertTrue(ratioReverseSequential > .85);
ex.removeAll();
for (; ex.getVolume().getStorage().getNextAvailablePage() < 22;) {
ex.to(random.nextInt()).store();
}
final float ratioRandom = inuseRatio(ex);
assertTrue(ratioRandom > .5 && ratioRandom < .75);
}
private float inuseRatio(final Exchange ex) throws Exception {
float total = 0;
float used = 0;
//
// forward sequential
//
for (long page = ex.clear().append(0).fetchBufferCopy(0).getPageAddress(); page < 20; page++) {
final Buffer buffer = ex.getBufferPool().get(ex.getVolume(), page, false, true);
if (buffer.isDataPage()) {
final int available = buffer.getAvailableSize();
System.out.println(buffer + " avail=" + available);
total = total + buffer.getBufferSize();
used = used + buffer.getBufferSize() - buffer.getAvailableSize();
}
buffer.releaseTouched();
}
return used / total;
}
}