Skip to content

Commit 54a9c40

Browse files
committed
Improve Interval B-Tree test coverage; add pprinter for B-Trees
Patch by Alex Petrov; reviewed by Benedict Elliott Smith for CASSANDRA-20766
1 parent e8bd231 commit 54a9c40

File tree

5 files changed

+342
-13
lines changed

5 files changed

+342
-13
lines changed

src/java/org/apache/cassandra/db/compaction/CompactionIterator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@
4040
import accord.local.RedundantBefore;
4141
import accord.utils.Invariants;
4242
import accord.utils.UnhandledEnum;
43-
import accord.utils.btree.BTree;
44-
import accord.utils.btree.BulkIterator;
45-
import accord.utils.btree.UpdateFunction;
4643
import org.apache.cassandra.config.DatabaseDescriptor;
4744
import org.apache.cassandra.cql3.ColumnIdentifier;
4845
import org.apache.cassandra.db.AbstractCompactionController;
@@ -105,9 +102,12 @@
105102
import org.apache.cassandra.service.accord.serializers.Version;
106103
import org.apache.cassandra.service.paxos.PaxosRepairHistory;
107104
import org.apache.cassandra.service.paxos.uncommitted.PaxosRows;
105+
import org.apache.cassandra.utils.BulkIterator;
108106
import org.apache.cassandra.utils.NoSpamLogger;
109107
import org.apache.cassandra.utils.NoSpamLogger.NoSpamLogStatement;
110108
import org.apache.cassandra.utils.TimeUUID;
109+
import org.apache.cassandra.utils.btree.BTree;
110+
import org.apache.cassandra.utils.btree.UpdateFunction;
111111

112112
import static accord.local.Cleanup.ERASE;
113113
import static accord.local.Cleanup.Input.PARTIAL;

src/java/org/apache/cassandra/service/accord/AbstractAccordSegmentCompactor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@
2626
import org.slf4j.LoggerFactory;
2727

2828
import accord.utils.Invariants;
29-
import accord.utils.btree.BTree;
30-
import accord.utils.btree.BulkIterator;
31-
import accord.utils.btree.UpdateFunction;
3229
import org.apache.cassandra.db.BufferClustering;
3330
import org.apache.cassandra.db.ColumnFamilyStore;
3431
import org.apache.cassandra.db.DecoratedKey;
@@ -50,10 +47,13 @@
5047
import org.apache.cassandra.service.ClientState;
5148
import org.apache.cassandra.service.accord.AccordJournalValueSerializers.FlyweightImage;
5249
import org.apache.cassandra.service.accord.AccordJournalValueSerializers.FlyweightSerializer;
50+
import org.apache.cassandra.utils.BulkIterator;
5351
import org.apache.cassandra.utils.NoSpamLogger;
5452

5553
import static java.util.concurrent.TimeUnit.MINUTES;
5654
import org.apache.cassandra.service.accord.serializers.Version;
55+
import org.apache.cassandra.utils.btree.BTree;
56+
import org.apache.cassandra.utils.btree.UpdateFunction;
5757

5858
/**
5959
* Segment compactor: takes static segments and compacts them into a single SSTable.

src/java/org/apache/cassandra/utils/btree/BTree.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ public class BTree
7171
*/
7272
public static final int BRANCH_SHIFT = BTREE_BRANCH_SHIFT.getInt();
7373

74+
/**
75+
* _DO NOT_ attempt to modify this field directly. Instead, use BRANCH_SHIFT above instead, as branch factor
76+
* should _always_ be a power of 2.
77+
*/
7478
static final int BRANCH_FACTOR = 1 << BRANCH_SHIFT;
7579
public static final int MIN_KEYS = BRANCH_FACTOR / 2 - 1;
7680
public static final int MAX_KEYS = BRANCH_FACTOR - 1;
@@ -4045,7 +4049,7 @@ BranchBuilder nonEmptyParentMaybeSteal(LeafOrBranchBuilder level)
40454049
* 2) If we exhaust all of our ancestors, and are not now ourselves overflowing, drain and return
40464050
* 3) Otherwise propagate the redistributed contents to our parent and return null, indicating we can continue to parent
40474051
*
4048-
* @return {@code null} if {@code parent} is still logicallly in use after we execute;
4052+
* @return {@code null} if {@code parent} is still logically in use after we execute;
40494053
* otherwise the return value is the final result
40504054
*/
40514055
private Object[] stealAndMaybeRepropagate(LeafOrBranchBuilder fill, BranchBuilder parent)
@@ -4148,7 +4152,8 @@ int seekInBranch(Object[] unode, int upos, int usz)
41484152
if (!remove.hasNext())
41494153
return -1 - (1 + usz);
41504154

4151-
int i = exponentialSearch(comparator, unode, upos, usz, remove.peek());
4155+
K next = remove.peek();
4156+
int i = exponentialSearch(comparator, unode, upos, usz, next);
41524157
if (i == -1 - usz)
41534158
{
41544159
// if we sort after the last key in the branch, we may need to descend into the right-most child
@@ -4159,7 +4164,7 @@ int seekInBranch(Object[] unode, int upos, int usz)
41594164
{
41604165
Object[] pnode = update.nodes[pdepth];
41614166
int ppos = update.positions[pdepth];
4162-
if (ppos < shallowSizeOfBranch(pnode) && comparator.compare(remove.peek(), (K)pnode[ppos]) >= 0)
4167+
if (ppos < shallowSizeOfBranch(pnode) && comparator.compare(next, (K)pnode[ppos]) >= 0)
41634168
{
41644169
// increase our result index to point to *after* the last child;
41654170
// (it's an inequality binary search semantic answer, so will be negated)
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.cassandra.utils.btree;
20+
21+
import static org.apache.cassandra.utils.btree.BTree.height;
22+
import static org.apache.cassandra.utils.btree.BTree.isEmpty;
23+
import static org.apache.cassandra.utils.btree.BTree.isLeaf;
24+
import static org.apache.cassandra.utils.btree.BTree.shallowSizeOfBranch;
25+
import static org.apache.cassandra.utils.btree.BTree.size;
26+
import static org.apache.cassandra.utils.btree.BTree.sizeMap;
27+
import static org.apache.cassandra.utils.btree.BTree.sizeOfLeaf;
28+
29+
// A small utility for debugging / printing B-Tree / IntervalBTrees
30+
//
31+
// Prints B-Tree in the following format:
32+
//
33+
// * (branch): [ 9,10:9 | 15,16:15 ]
34+
// ├─ [-∞]
35+
// │ * (branch): [ 3,4:3 | 6,7:6 ]
36+
// │ ├─ [-∞]
37+
// │ │ (leaf): [ 0,1:0, 1,2:1, 2,3:2 ]
38+
// │ ├─ [3,4:3]
39+
// │ │ (leaf): [ 4,5:4, 5,6:5 ]
40+
// │ └─ [6,7:6]
41+
// │ (leaf): [ 7,8:7, 8,9:8 ]
42+
// ├─ [9,10:9]
43+
// │ * (branch): [ 12,13:12 ]
44+
// │ ├─ [-∞]
45+
// │ │ (leaf): [ 10,11:10, 11,12:11 ]
46+
// │ └─ [12,13:12]
47+
// │ (leaf): [ 13,14:13, 14,15:14 ]
48+
// └─ [15,16:15]
49+
// * (branch): [ 18,19:18 ]
50+
// ├─ [-∞]
51+
// │ (leaf): [ 16,17:16, 17,18:17 ]
52+
// └─ [18,19:18]
53+
// (leaf): [ 19,20:19 ]
54+
@SuppressWarnings("unused")
55+
public class BTreePrinter
56+
{
57+
private static boolean PRINT_SIZE_MAP = false;
58+
59+
public static String print(Object[] btree)
60+
{
61+
if (isEmpty(btree))
62+
return "empty";
63+
64+
StringBuilder sb = new StringBuilder();
65+
sb.append("(size=").append(size(btree))
66+
.append(", height=").append(height(btree))
67+
.append("):\n");
68+
printNode(sb, btree, 0, "");
69+
return sb.toString();
70+
}
71+
72+
private static void printNode(StringBuilder sb, Object[] node, int level, String prefix)
73+
{
74+
String indent = " ".repeat(level);
75+
76+
if (isLeaf(node))
77+
{
78+
int leafSize = sizeOfLeaf(node);
79+
sb.append(prefix).append(indent)
80+
.append("(leaf): [ ");
81+
for (int i = 0; i < leafSize; i++)
82+
{
83+
if (i > 0) sb.append(", ");
84+
sb.append(node[i]);
85+
}
86+
sb.append(" ]\n");
87+
}
88+
else
89+
{
90+
int keyCount = shallowSizeOfBranch(node);
91+
int childCount = keyCount + 1;
92+
int[] sizeMap = sizeMap(node);
93+
94+
sb.append(prefix)
95+
.append(indent)
96+
.append("* (branch): [ ");
97+
98+
for (int i = 0; i < keyCount; i++)
99+
{
100+
if (i > 0) sb.append(" | ");
101+
sb.append(node[i]);
102+
}
103+
sb.append(" ]\n");
104+
105+
if (PRINT_SIZE_MAP)
106+
{
107+
sb.append(prefix).append(indent).append("├─ sizeMap: ");
108+
for (int i = 0; i < sizeMap.length; i++)
109+
{
110+
if (i > 0) sb.append(", ");
111+
sb.append(sizeMap[i]);
112+
}
113+
sb.append("\n");
114+
}
115+
116+
for (int i = 0; i < childCount; i++)
117+
{
118+
Object[] child = (Object[]) node[keyCount + i];
119+
String childPrefix = prefix + indent;
120+
String verticalLine = (i == childCount - 1) ? "└─ " : "├─ ";
121+
String nextPrefix = (i == childCount - 1) ? " " : "│ ";
122+
123+
sb.append(childPrefix).append(verticalLine)
124+
.append("[")
125+
.append(i == 0 ? "-∞" : node[i - 1])
126+
.append("]\n");
127+
128+
// Recursing here should be fine, as we do not expect depth to be too large
129+
printNode(sb, child, level + 1, prefix + indent + nextPrefix);
130+
}
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)