2
* Copyright © 2012 Akiban Technologies, Inc. All rights reserved.
4
* This program and the accompanying materials are made available
5
* under the terms of the Eclipse Public License v1.0 which
6
* accompanies this distribution, and is available at
7
* http://www.eclipse.org/legal/epl-v10.html
9
* This program may also be available under different license terms.
10
* For more information, see www.akiban.com or contact licensing@akiban.com.
13
* Akiban Technologies, Inc.
16
package com.persistit;
18
import static org.junit.Assert.assertEquals;
20
import org.junit.Test;
22
import com.persistit.exception.PersistitException;
23
import com.persistit.policy.SplitPolicy;
25
public class ExchangeRebalanceTest extends PersistitUnitTestCase {
27
final StringBuilder sb = new StringBuilder();
30
public void setUp() throws Exception {
32
while (sb.length() < Buffer.MAX_BUFFER_SIZE) {
38
* Construct a tree with two adjacent pages that are nearly full such that
39
* the second key B of the right page is long and has very small elided byte
40
* count relative to the first key A on that page. Removing key A requires
41
* the left sibling to have B as its max key. If B won't fit on the left
42
* page, then Persistit splits the left page to make room for it. Note that
43
* changes in key or page structure will likely break this test.
48
public void testRebalanceException() throws Exception {
49
final Exchange exchange = _persistit.getExchange("persistit", "rebalance", true);
50
exchange.setSplitPolicy(SplitPolicy.LEFT_BIAS);
51
setUpPrettyFullBuffers(exchange, false, RED_FOX.length(), true);
52
System.out.printf("\ntestRebalanceException\n");
53
_persistit.checkAllVolumes();
54
final int beforeRemove = countSiblings(exchange, 0);
55
exchange.clear().append("b").previous(true);
57
_persistit.getCleanupManager().poll();
58
_persistit.checkAllVolumes();
59
final int afterRemove = countSiblings(exchange, 0);
60
assertEquals("Remove should have caused rebalance", beforeRemove + 1, afterRemove);
64
* Similar logic to {@link #testRebalanceException()} except that the keys
65
* are carefully constructed on the index leaf level rather than the data
66
* level. To do this we use three long-ish records per major key in the data
67
* pages so that the key pattern in the index leaf table aligns as desired.
72
public void testRebalanceIndexException() throws Exception {
73
final Exchange exchange = _persistit.getExchange("persistit", "rebalance", true);
74
exchange.setSplitPolicy(SplitPolicy.LEFT_BIAS);
75
setUpPrettyFullBuffers(exchange, true, 0, true);
76
System.out.printf("\ntestRebalanceIndexException\n");
77
_persistit.checkAllVolumes();
78
final int beforeRemove = countSiblings(exchange, 1);
80
exchange.clear().append("b").previous(true);
82
exchange.remove(Key.GT);
83
_persistit.getCleanupManager().poll();
84
_persistit.checkAllVolumes();
85
final int afterRemove = countSiblings(exchange, 1);
86
assertEquals("Remove should have caused rebalance", beforeRemove + 1, afterRemove);
90
private void setUpPrettyFullBuffers(final Exchange ex, final boolean asIndex, final int valueLength,
91
final boolean discontinuous) throws PersistitException {
93
final int depth = asIndex ? 1 : 0;
97
// load page A with keys of increasing length
99
setUpDeepKey(ex, 'a', a);
100
storeValue(ex, valueLength, asIndex);
101
if (ex.getTree().getDepth() > depth) {
102
final Buffer b1 = ex.fetchBufferCopy(depth);
103
// Stop when nearly full
104
if (b1.getAvailableSize() < valueLength + 20) {
107
leftPage = b1.getPageAddress();
111
// load additional keys into page B
112
for (b = a + 1;; b++) {
113
if (ex.getTree().getDepth() > depth) {
114
final Buffer b2 = ex.fetchBufferCopy(depth);
115
if (b2.getPageAddress() != leftPage && b2.getAvailableSize() < valueLength + 20) {
119
setUpDeepKey(ex, discontinuous && b > a ? 'b' : 'a', b);
120
storeValue(ex, valueLength, asIndex);
124
private void setUpDeepKey(final Exchange ex, final char fill, final int n) {
125
ex.getKey().clear().append(keyString(fill, n, n - 34, 4, n)).append(1);
128
private void setupValue(final Exchange ex, final int valueLength) {
129
ex.getValue().put(sb.toString().substring(0, valueLength));
132
private void storeValue(final Exchange ex, final int valueLength, final boolean asIndex) throws PersistitException {
134
// Fill up one data page so that the base key will be inserted into
136
setupValue(ex, ex.getBufferPool().getBufferSize() / 4);
137
for (int i = 1; i < 4; i++) {
138
ex.cut().append(i).store();
141
setupValue(ex, valueLength);
146
String keyString(final char fill, final int length, final int prefix, final int width, final int k) {
147
final StringBuilder sb = new StringBuilder();
148
for (int i = 0; i < prefix && i < length; i++) {
151
sb.append(String.format("%0" + width + "d", k));
152
for (int i = length - sb.length(); i < length; i++) {
155
sb.setLength(length);
156
return sb.toString();
159
private int countSiblings(final Exchange exchange, final int level) throws PersistitException {
161
exchange.clear().append(Key.BEFORE);
162
Buffer b = exchange.fetchBufferCopy(level);
165
final long rightSibling = b.getRightSibling();
166
if (rightSibling != 0) {
167
b = exchange.getBufferPool().getBufferCopy(exchange.getVolume(), rightSibling);