-
Notifications
You must be signed in to change notification settings - Fork 6
/
colltree.go
116 lines (103 loc) · 3.09 KB
/
colltree.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sbtree
import (
"fmt"
"v.io/v23/context"
wire "v.io/v23/services/syncbase"
"v.io/v23/syncbase"
"v.io/v23/vdl"
)
// CollectionTree has all the data for the collection page of the Syncbase debug
// viewer.
type CollectionTree struct {
Service syncbase.Service
Database syncbase.Database
Collection syncbase.Collection
RowCount int
TotKeySize uint64
KeysPage keysPage
}
type keyVal struct {
Index int
Key string
Value interface{}
}
// keysPage is a contiguous subset of the keys, used for pagination.
type keysPage struct {
HasPrev bool
KeyVals []keyVal
NextKey string
}
// AssembleCollectionTree returns information describing the given Syncbase
// collection, including a "page" of keys starting at the given first key.
func AssembleCollectionTree(
ctx *context.T, server, dbBlessing, dbName, collBlessing, collName, firstKey string, keysPerPage int,
) *CollectionTree {
var (
dbID = wire.Id{Blessing: dbBlessing, Name: dbName}
collID = wire.Id{Blessing: collBlessing, Name: collName}
service = syncbase.NewService(server)
// TODO(eobrain) Confirm nil for schema is appropriate
db = service.DatabaseForId(dbID, nil)
coll = db.CollectionForId(collID)
)
rowCount, totKeySize, page := scanCollection(ctx, coll, firstKey, keysPerPage)
return &CollectionTree{service, db, coll, rowCount, totKeySize, page}
}
// scanCollection gets some statistics, plus a page of key-value pairs starting
// with firstKey.
func scanCollection(
ctx *context.T, coll syncbase.Collection, firstKey string, keysPerPage int,
) (rowCount int, totKeySize uint64, page keysPage) {
page.KeyVals = make([]keyVal, 0, keysPerPage)
stream := coll.Scan(ctx, syncbase.Prefix(""))
// We scan through all the keys lexicographically, and when we come to a
// key >= firstKey we start gathering a "page" of keys. As we scan, a
// state machine keeps track of whether we are before the page, in the
// page, or after the page.
const (
before = 0
gathering = iota
done = iota
)
state := before
for stream.Advance() {
key := stream.Key()
totKeySize += uint64(len(key))
switch state {
case before:
if key >= firstKey {
// First key found: transition to gathering the page
state = gathering
} else {
// There is at least one key before the page
page.HasPrev = true
}
case gathering:
if len(page.KeyVals) >= keysPerPage {
// Page full: transition to done. There is at
// least one key after the page.
state = done
page.NextKey = key
}
case done:
// Done gathering. Terminal state.
}
if state == gathering {
// Grab the value, put it and the key into a KeyVal, and
// add it to the page.
kv := keyVal{Index: rowCount, Key: key}
var value *vdl.Value
if err := stream.Value(&value); err != nil {
kv.Value = fmt.Sprintf("ERROR getting value: %v", err)
} else {
kv.Value = value
}
page.KeyVals = append(page.KeyVals, kv)
}
rowCount++
}
return
}