Skip to content

Commit 83f9a95

Browse files
authored
Merge pull request #141 from ipfs/fix/ancestor-sep
Only count a key as an ancestor if there is a separator
2 parents e9f6023 + 2fe8b68 commit 83f9a95

File tree

11 files changed

+271
-108
lines changed

11 files changed

+271
-108
lines changed

key.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,20 +212,27 @@ func (k Key) ChildString(s string) Key {
212212
// NewKey("/Comedy").IsAncestorOf("/Comedy/MontyPython")
213213
// true
214214
func (k Key) IsAncestorOf(other Key) bool {
215-
if other.string == k.string {
215+
// equivalent to HasPrefix(other, k.string + "/")
216+
217+
if len(other.string) <= len(k.string) {
218+
// We're not long enough to be a child.
216219
return false
217220
}
218-
return strings.HasPrefix(other.string, k.string)
221+
222+
if k.string == "/" {
223+
// We're the root and the other key is longer.
224+
return true
225+
}
226+
227+
// "other" starts with /k.string/
228+
return other.string[len(k.string)] == '/' && other.string[:len(k.string)] == k.string
219229
}
220230

221231
// IsDescendantOf returns whether this key contains another as a prefix.
222232
// NewKey("/Comedy/MontyPython").IsDescendantOf("/Comedy")
223233
// true
224234
func (k Key) IsDescendantOf(other Key) bool {
225-
if other.string == k.string {
226-
return false
227-
}
228-
return strings.HasPrefix(k.string, other.string)
235+
return other.IsAncestorOf(k)
229236
}
230237

231238
// IsTopLevel returns whether this key has only one namespace.

key_test.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,19 +84,23 @@ func CheckTrue(c *C, cond bool) {
8484
func (ks *KeySuite) TestKeyAncestry(c *C) {
8585
k1 := NewKey("/A/B/C")
8686
k2 := NewKey("/A/B/C/D")
87+
k3 := NewKey("/AB")
88+
k4 := NewKey("/A")
8789

8890
c.Check(k1.String(), Equals, "/A/B/C")
8991
c.Check(k2.String(), Equals, "/A/B/C/D")
9092
CheckTrue(c, k1.IsAncestorOf(k2))
9193
CheckTrue(c, k2.IsDescendantOf(k1))
92-
CheckTrue(c, NewKey("/A").IsAncestorOf(k2))
93-
CheckTrue(c, NewKey("/A").IsAncestorOf(k1))
94-
CheckTrue(c, !NewKey("/A").IsDescendantOf(k2))
95-
CheckTrue(c, !NewKey("/A").IsDescendantOf(k1))
96-
CheckTrue(c, k2.IsDescendantOf(NewKey("/A")))
97-
CheckTrue(c, k1.IsDescendantOf(NewKey("/A")))
98-
CheckTrue(c, !k2.IsAncestorOf(NewKey("/A")))
99-
CheckTrue(c, !k1.IsAncestorOf(NewKey("/A")))
94+
CheckTrue(c, k4.IsAncestorOf(k2))
95+
CheckTrue(c, k4.IsAncestorOf(k1))
96+
CheckTrue(c, !k4.IsDescendantOf(k2))
97+
CheckTrue(c, !k4.IsDescendantOf(k1))
98+
CheckTrue(c, !k3.IsDescendantOf(k4))
99+
CheckTrue(c, !k4.IsAncestorOf(k3))
100+
CheckTrue(c, k2.IsDescendantOf(k4))
101+
CheckTrue(c, k1.IsDescendantOf(k4))
102+
CheckTrue(c, !k2.IsAncestorOf(k4))
103+
CheckTrue(c, !k1.IsAncestorOf(k4))
100104
CheckTrue(c, !k2.IsAncestorOf(k2))
101105
CheckTrue(c, !k1.IsAncestorOf(k1))
102106
c.Check(k1.Child(NewKey("D")).String(), Equals, k2.String())

mount/lookup_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package mount
2+
3+
import (
4+
"testing"
5+
6+
datastore "github.com/ipfs/go-datastore"
7+
)
8+
9+
func TestLookup(t *testing.T) {
10+
mapds0 := datastore.NewMapDatastore()
11+
mapds1 := datastore.NewMapDatastore()
12+
mapds2 := datastore.NewMapDatastore()
13+
mapds3 := datastore.NewMapDatastore()
14+
m := New([]Mount{
15+
{Prefix: datastore.NewKey("/"), Datastore: mapds0},
16+
{Prefix: datastore.NewKey("/foo"), Datastore: mapds1},
17+
{Prefix: datastore.NewKey("/bar"), Datastore: mapds2},
18+
{Prefix: datastore.NewKey("/baz"), Datastore: mapds3},
19+
})
20+
_, mnts, _ := m.lookupAll(datastore.NewKey("/bar"))
21+
if len(mnts) != 1 || mnts[0] != datastore.NewKey("/bar") {
22+
t.Errorf("expected to find the mountpoint /bar, got %v", mnts)
23+
}
24+
25+
_, mnts, _ = m.lookupAll(datastore.NewKey("/fo"))
26+
if len(mnts) != 1 || mnts[0] != datastore.NewKey("/") {
27+
t.Errorf("expected to find the mountpoint /, got %v", mnts)
28+
}
29+
30+
_, mnt, _ := m.lookup(datastore.NewKey("/fo"))
31+
if mnt != datastore.NewKey("/") {
32+
t.Errorf("expected to find the mountpoint /, got %v", mnt)
33+
}
34+
35+
// /foo lives in /, /foo/bar lives in /foo. Most systems don't let us use the key "" or /.
36+
_, mnt, _ = m.lookup(datastore.NewKey("/foo"))
37+
if mnt != datastore.NewKey("/") {
38+
t.Errorf("expected to find the mountpoint /, got %v", mnt)
39+
}
40+
41+
_, mnt, _ = m.lookup(datastore.NewKey("/foo/bar"))
42+
if mnt != datastore.NewKey("/foo") {
43+
t.Errorf("expected to find the mountpoint /foo, got %v", mnt)
44+
}
45+
}

mount/mount.go

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,18 @@ var (
1818
ErrNoMount = errors.New("no datastore mounted for this key")
1919
)
2020

21+
// Mount defines a datastore mount. It mounts the given datastore at the given
22+
// prefix.
2123
type Mount struct {
2224
Prefix ds.Key
2325
Datastore ds.Datastore
2426
}
2527

28+
// New creates a new mount datstore from the given mounts. See the documentation
29+
// on Datastore for details.
30+
//
31+
// The order of the mounts does not matter, they will be applied most specific
32+
// to least specific.
2633
func New(mounts []Mount) *Datastore {
2734
// make a copy so we're sure it doesn't mutate
2835
m := make([]Mount, len(mounts))
@@ -31,15 +38,39 @@ func New(mounts []Mount) *Datastore {
3138
return &Datastore{mounts: m}
3239
}
3340

41+
// Datastore is a mount datastore. In this datastore, keys live under the most
42+
// specific mounted sub-datastore. That is, given sub-datastores mounted under:
43+
//
44+
// * /
45+
// * /foo
46+
// * /foo/bar
47+
//
48+
// Keys would be written as follows:
49+
//
50+
// * /foo, /foobar, /baz would all live under /.
51+
// * /foo/baz, /foo/bar, etc. would live under /foo.
52+
// * /foo/bar/baz would live under /foo/bar.
53+
//
54+
// Additionally, even if the datastore mounted at / contains the key /foo/thing,
55+
// the datastore mounted at /foo would mask this value in get, deletes, and
56+
// query results.
57+
//
58+
// Finally, if no root (/) mount is provided, operations on keys living outside
59+
// all of the provided mounts will behave as follows:
60+
//
61+
// * Get - Returns datastore.ErrNotFound.
62+
// * Query - Returns no results.
63+
// * Put - Returns ErrNoMount.
3464
type Datastore struct {
3565
mounts []Mount
3666
}
3767

3868
var _ ds.Datastore = (*Datastore)(nil)
3969

70+
// lookup looks up the datastore in which the given key lives.
4071
func (d *Datastore) lookup(key ds.Key) (ds.Datastore, ds.Key, ds.Key) {
4172
for _, m := range d.mounts {
42-
if m.Prefix.Equal(key) || m.Prefix.IsAncestorOf(key) {
73+
if m.Prefix.IsAncestorOf(key) {
4374
s := strings.TrimPrefix(key.String(), m.Prefix.String())
4475
k := ds.NewKey(s)
4576
return m.Datastore, m.Prefix, k
@@ -147,38 +178,58 @@ func (h *querySet) next() (query.Result, bool) {
147178
return next, true
148179
}
149180

150-
// lookupAll returns all mounts that might contain keys that are descendant of <key>
181+
// lookupAll returns all mounts that might contain keys that are strict
182+
// descendants of <key>. It will not return mounts that match key exactly.
183+
//
184+
// Specifically, this function will return three slices:
185+
//
186+
// * The matching datastores.
187+
// * The prefixes where each matching datastore has been mounted.
188+
// * The prefix within these datastores at which descendants of the passed key
189+
// live. If the mounted datastore is fully contained within the given key,
190+
// this will be /.
191+
//
192+
// By example, given the datastores:
151193
//
152-
// Matching: /ao/e
194+
// * / - root
195+
// * /foo -
196+
// * /bar
197+
// * /foo/bar
153198
//
154-
// / B /ao/e
155-
// /a/ not matching
156-
// /ao/ B /e
157-
// /ao/e/ A /
158-
// /ao/e/uh/ A /
159-
// /aoe/ not matching
199+
// This function function will behave as follows:
200+
//
201+
// * key -> ([mountpoints], [rests]) # comment
202+
// * / -> ([/, /foo, /bar, /foo/bar], [/, /, /, /]) # all datastores
203+
// * /foo -> ([/foo, /foo/bar], [/, /]) # all datastores under /foo
204+
// * /foo/bar -> ([/foo/bar], [/]) # /foo/bar
205+
// * /bar/foo -> ([/bar], [/foo]) # the datastore mounted at /bar, rest is /foo
206+
// * /ba -> ([/], [/]) # the root; only full components are matched.
160207
func (d *Datastore) lookupAll(key ds.Key) (dst []ds.Datastore, mountpoint, rest []ds.Key) {
161208
for _, m := range d.mounts {
162-
p := m.Prefix.String()
163-
if len(p) > 1 {
164-
p = p + "/"
165-
}
166-
167-
if strings.HasPrefix(p, key.String()) {
209+
if m.Prefix.IsDescendantOf(key) {
168210
dst = append(dst, m.Datastore)
169211
mountpoint = append(mountpoint, m.Prefix)
170212
rest = append(rest, ds.NewKey("/"))
171-
} else if strings.HasPrefix(key.String(), p) {
213+
} else if m.Prefix.Equal(key) || m.Prefix.IsAncestorOf(key) {
172214
r := strings.TrimPrefix(key.String(), m.Prefix.String())
173215

174216
dst = append(dst, m.Datastore)
175217
mountpoint = append(mountpoint, m.Prefix)
176218
rest = append(rest, ds.NewKey(r))
219+
220+
// We've found an ancestor (or equal) key. We might have
221+
// more general datastores, but they won't contain keys
222+
// with this prefix so there's no point in searching them.
223+
break
177224
}
178225
}
179226
return dst, mountpoint, rest
180227
}
181228

229+
// Put puts the given value into the datastore at the given key.
230+
//
231+
// Returns ErrNoMount if there no datastores are mounted at the appropriate
232+
// prefix for the given key.
182233
func (d *Datastore) Put(key ds.Key, value []byte) error {
183234
cds, _, k := d.lookup(key)
184235
if cds == nil {
@@ -191,20 +242,17 @@ func (d *Datastore) Put(key ds.Key, value []byte) error {
191242
func (d *Datastore) Sync(prefix ds.Key) error {
192243
// Sync all mount points below the prefix
193244
// Sync the mount point right at (or above) the prefix
194-
dstores, mountPts, rest := d.lookupAll(prefix)
245+
dstores, _, rest := d.lookupAll(prefix)
195246
for i, suffix := range rest {
196247
if err := dstores[i].Sync(suffix); err != nil {
197248
return err
198249
}
199-
200-
if mountPts[i].Equal(prefix) || suffix.String() != "/" {
201-
return nil
202-
}
203250
}
204251

205252
return nil
206253
}
207254

255+
// Get returns the value associated with the key from the appropriate datastore.
208256
func (d *Datastore) Get(key ds.Key) (value []byte, err error) {
209257
cds, _, k := d.lookup(key)
210258
if cds == nil {
@@ -213,6 +261,8 @@ func (d *Datastore) Get(key ds.Key) (value []byte, err error) {
213261
return cds.Get(k)
214262
}
215263

264+
// Has returns the true if there exists a value associated with key in the
265+
// appropriate datastore.
216266
func (d *Datastore) Has(key ds.Key) (exists bool, err error) {
217267
cds, _, k := d.lookup(key)
218268
if cds == nil {
@@ -221,6 +271,8 @@ func (d *Datastore) Has(key ds.Key) (exists bool, err error) {
221271
return cds.Has(k)
222272
}
223273

274+
// Get returns the size of the value associated with the key in the appropriate
275+
// datastore.
224276
func (d *Datastore) GetSize(key ds.Key) (size int, err error) {
225277
cds, _, k := d.lookup(key)
226278
if cds == nil {
@@ -229,6 +281,10 @@ func (d *Datastore) GetSize(key ds.Key) (size int, err error) {
229281
return cds.GetSize(k)
230282
}
231283

284+
// Delete deletes the value associated with the key in the appropriate
285+
// datastore.
286+
//
287+
// Delete returns no error if there is no value associated with the given key.
232288
func (d *Datastore) Delete(key ds.Key) error {
233289
cds, _, k := d.lookup(key)
234290
if cds == nil {
@@ -237,6 +293,11 @@ func (d *Datastore) Delete(key ds.Key) error {
237293
return cds.Delete(k)
238294
}
239295

296+
// Query queries the appropriate mounted datastores, merging the results
297+
// according to the given orders.
298+
//
299+
// If a query prefix is specified, Query will avoid querying datastores mounted
300+
// outside that prefix.
240301
func (d *Datastore) Query(master query.Query) (query.Results, error) {
241302
childQuery := query.Query{
242303
Prefix: master.Prefix,
@@ -292,6 +353,7 @@ func (d *Datastore) Query(master query.Query) (query.Results, error) {
292353
return qr, nil
293354
}
294355

356+
// Close closes all mounted datastores.
295357
func (d *Datastore) Close() error {
296358
for _, d := range d.mounts {
297359
err := d.Datastore.Close()
@@ -323,6 +385,7 @@ type mountBatch struct {
323385
d *Datastore
324386
}
325387

388+
// Batch returns a batch that operates over all mounted datastores.
326389
func (d *Datastore) Batch() (ds.Batch, error) {
327390
return &mountBatch{
328391
mounts: make(map[string]ds.Batch),

0 commit comments

Comments
 (0)