Skip to content

Commit 5449ae6

Browse files
Merge pull request #78 from Workiva/palm_query
Added query functionality and some benchmarks.
2 parents ed20756 + ace5c26 commit 5449ae6

File tree

5 files changed

+168
-4
lines changed

5 files changed

+168
-4
lines changed

btree/palm/action.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,41 @@ func newRemoveAction(keys common.Comparators) *removeAction {
122122
}
123123
}
124124

125+
type applyAction struct {
126+
fn func(common.Comparator) bool
127+
start, stop common.Comparator
128+
completer *sync.WaitGroup
129+
}
130+
131+
func (aa *applyAction) operation() operation {
132+
return apply
133+
}
134+
135+
func (aa *applyAction) nodes() []*node {
136+
return nil
137+
}
138+
139+
func (aa *applyAction) addNode(i int64, n *node) {}
140+
141+
func (aa *applyAction) keys() common.Comparators {
142+
return nil
143+
}
144+
145+
func (aa *applyAction) complete() {
146+
aa.completer.Done()
147+
}
148+
149+
func newApplyAction(fn func(common.Comparator) bool, start, stop common.Comparator) *applyAction {
150+
aa := &applyAction{
151+
fn: fn,
152+
start: start,
153+
stop: stop,
154+
completer: new(sync.WaitGroup),
155+
}
156+
aa.completer.Add(1)
157+
return aa
158+
}
159+
125160
func minUint64(choices ...uint64) uint64 {
126161
min := choices[0]
127162
for i := 1; i < len(choices); i++ {

btree/palm/interface.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ BenchmarkGet-8 2000000 629 ns/op
3939
BenchmarkBulkGet-8 5000 223249 ns/op
4040
BenchmarkDelete-8 500000 2421 ns/op
4141
BenchmarkBulkDelete-8 500 2790461 ns/op
42-
42+
BenchmarkFindQuery-8 1000000 1166 ns/op
43+
BenchmarkExecuteQuery-8 10000 1290732 ns/op
4344
4445
*/
4546
package palm
@@ -58,6 +59,10 @@ type BTree interface {
5859
Get(...common.Comparator) common.Comparators
5960
// Len returns the number of items in the tree.
6061
Len() uint64
62+
// Query will return a list of Comparators that fall within the
63+
// provided start and stop Comparators. Start is inclusive while
64+
// stop is exclusive, ie [start, stop).
65+
Query(start, stop common.Comparator) common.Comparators
6166
// Dispose will clean up any resources used by this tree. This
6267
// must be called to prevent a memory leak.
6368
Dispose()

btree/palm/node.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ func (n *node) splitLeaf(i, capacity uint64) (common.Comparator, *node, *node) {
195195
keys: rightKeys,
196196
nodes: newNodes(uint64(cap(n.nodes.list))),
197197
isLeaf: true,
198+
right: n.right,
198199
}
199200
n.right = nn
200201
return key, n, nn

btree/palm/tree.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const (
3232
get operation = iota
3333
add
3434
remove
35+
apply
3536
)
3637

3738
const multiThreadAt = 1000 // number of keys before we multithread lookups
@@ -89,6 +90,12 @@ func (ptree *ptree) checkAndRun(action action) {
8990
} else {
9091
ptree.operationRunner(interfaces{action}, false)
9192
}
93+
case apply:
94+
q := action.(*applyAction)
95+
n := getParent(ptree.root, q.start)
96+
ptree.apply(n, q)
97+
q.complete()
98+
ptree.reset()
9299
}
93100
} else {
94101
ptree.actions.Put(action)
@@ -154,14 +161,33 @@ func (ptree *ptree) fetchKeys(xns interfaces, inParallel bool) (map[*node][]*key
154161
deleteOperations[n] = append(deleteOperations[n], &keyBundle{key: action.keys()[i]})
155162
}
156163
toComplete = append(toComplete, action)
157-
case get:
164+
case get, apply:
158165
action.complete()
159166
}
160167
}
161168

162169
return writeOperations, deleteOperations, toComplete
163170
}
164171

172+
func (ptree *ptree) apply(n *node, aa *applyAction) {
173+
i := n.search(aa.start)
174+
if i == n.keys.len() { // nothing to apply against
175+
return
176+
}
177+
178+
var k common.Comparator
179+
for n != nil {
180+
for j := i; j < n.keys.len(); j++ {
181+
k = n.keys.byPosition(j)
182+
if aa.stop.Compare(k) < 1 || !aa.fn(k) {
183+
return
184+
}
185+
}
186+
n = n.right
187+
i = 0
188+
}
189+
}
190+
165191
func (ptree *ptree) fetchKeysInSerial(xns interfaces) {
166192
for _, ifc := range xns {
167193
action := ifc.(action)
@@ -181,6 +207,9 @@ func (ptree *ptree) fetchKeysInSerial(xns interfaces) {
181207
action.keys()[i] = k
182208
}
183209
}
210+
case apply:
211+
q := action.(*applyAction)
212+
ptree.apply(n, q)
184213
}
185214
}
186215
}
@@ -245,6 +274,9 @@ func (ptree *ptree) fetchKeysInParallel(xns []interface{}) {
245274
action.keys()[j] = k
246275
}
247276
}
277+
case apply:
278+
q := action.(*applyAction)
279+
ptree.apply(n, q)
248280
}
249281
}
250282
wg.Done()
@@ -418,6 +450,20 @@ func (ptree *ptree) Len() uint64 {
418450
return atomic.LoadUint64(&ptree.number)
419451
}
420452

453+
// Query will return a list of Comparators that fall within the
454+
// provided start and stop Comparators. Start is inclusive while
455+
// stop is exclusive, ie [start, stop).
456+
func (ptree *ptree) Query(start, stop common.Comparator) common.Comparators {
457+
cmps := make(common.Comparators, 0, 32)
458+
aa := newApplyAction(func(cmp common.Comparator) bool {
459+
cmps = append(cmps, cmp)
460+
return true
461+
}, start, stop)
462+
ptree.checkAndRun(aa)
463+
aa.completer.Wait()
464+
return cmps
465+
}
466+
421467
// Dispose will clean up any resources used by this tree. This
422468
// must be called to prevent a memory leak.
423469
func (ptree *ptree) Dispose() {

btree/palm/tree_test.go

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,57 @@ func TestInsertAndDeletesWithSplits(t *testing.T) {
379379
assert.Equal(t, keys2, tree.Get(keys2...))
380380
}
381381

382+
func TestSimpleQuery(t *testing.T) {
383+
tree := newTree(3, 3)
384+
defer tree.Dispose()
385+
m1 := mockKey(1)
386+
tree.Insert(m1)
387+
388+
result := tree.Query(mockKey(0), mockKey(5))
389+
assert.Equal(t, common.Comparators{m1}, result)
390+
391+
result = tree.Query(mockKey(0), mockKey(1))
392+
assert.Len(t, result, 0)
393+
394+
result = tree.Query(mockKey(2), mockKey(10))
395+
assert.Len(t, result, 0)
396+
397+
result = tree.Query(mockKey(1), mockKey(10))
398+
assert.Equal(t, common.Comparators{m1}, result)
399+
}
400+
401+
func TestMultipleQuery(t *testing.T) {
402+
tree := newTree(3, 3)
403+
defer tree.Dispose()
404+
m1 := mockKey(1)
405+
m2 := mockKey(5)
406+
tree.Insert(m1, m2)
407+
408+
result := tree.Query(mockKey(0), mockKey(10))
409+
assert.Equal(t, common.Comparators{m1, m2}, result)
410+
411+
result = tree.Query(mockKey(1), mockKey(5))
412+
assert.Equal(t, common.Comparators{m1}, result)
413+
414+
result = tree.Query(mockKey(6), mockKey(10))
415+
assert.Len(t, result, 0)
416+
417+
result = tree.Query(mockKey(5), mockKey(10))
418+
assert.Equal(t, common.Comparators{m2}, result)
419+
}
420+
421+
func TestCrossNodeQuery(t *testing.T) {
422+
tree := newTree(3, 3)
423+
defer tree.Dispose()
424+
keys := generateKeys(100)
425+
tree.Insert(keys...)
426+
427+
result := tree.Query(mockKey(0), mockKey(len(keys)))
428+
if !assert.Equal(t, keys, result) {
429+
tree.print(getConsoleLogger())
430+
}
431+
}
432+
382433
func BenchmarkReadAndWrites(b *testing.B) {
383434
numItems := 1000
384435
keys := make([]common.Comparators, 0, b.N)
@@ -475,9 +526,9 @@ func BenchmarkGet(b *testing.B) {
475526
}
476527

477528
func BenchmarkBulkGet(b *testing.B) {
478-
numItems := 1000
529+
numItems := b.N
479530
keys := generateRandomKeys(numItems)
480-
tree := newTree(16, 8)
531+
tree := newTree(8, 8)
481532
tree.Insert(keys...)
482533

483534
b.ResetTimer()
@@ -516,3 +567,29 @@ func BenchmarkBulkDelete(b *testing.B) {
516567
trees[i].Delete(keys...)
517568
}
518569
}
570+
571+
func BenchmarkFindQuery(b *testing.B) {
572+
numItems := b.N
573+
keys := generateKeys(numItems)
574+
tree := newTree(8, 8)
575+
tree.Insert(keys...)
576+
577+
b.ResetTimer()
578+
579+
for i := 0; i < b.N; i++ {
580+
tree.Query(mockKey(numItems/2), mockKey(numItems/2+1))
581+
}
582+
}
583+
584+
func BenchmarkExecuteQuery(b *testing.B) {
585+
numItems := b.N
586+
keys := generateKeys(numItems)
587+
tree := newTree(8, 8)
588+
tree.Insert(keys...)
589+
590+
b.ResetTimer()
591+
592+
for i := 0; i < b.N; i++ {
593+
tree.Query(mockKey(0), mockKey(numItems))
594+
}
595+
}

0 commit comments

Comments
 (0)