Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SearchValue #174

Merged
merged 11 commits into from
Aug 10, 2018
Merged

Implement SearchValue #174

merged 11 commits into from
Aug 10, 2018

Conversation

magik6k
Copy link
Contributor

@magik6k magik6k commented Jul 17, 2018

This exposes new function which streams entries as they are found - https://github.com/libp2p/go-libp2p-routing/blob/master/routing.go#L61

Part of ipfs/kubo#5232
Replaces #49

TODO:

  • More tests
  • Error handling feels weird

@ghost ghost assigned magik6k Jul 17, 2018
@ghost ghost added the status/in-progress In progress label Jul 17, 2018
@Stebalien
Copy link
Member

Tell me when you want this reviewed.

@magik6k magik6k force-pushed the feat/searchvalue branch 3 times, most recently from bcb8474 to 75bae8d Compare July 18, 2018 12:47
@magik6k magik6k requested a review from Stebalien July 18, 2018 13:39
@magik6k
Copy link
Contributor Author

magik6k commented Jul 18, 2018

Should be ready

routing.go Outdated
return nil, routing.ErrNotFound
responsesNeeded := 0
if !cfg.Offline {
responsesNeeded = getQuorum(&cfg)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we should be applying the quorum in here. That's more of a GetValue thing. Really, we should probably have a separate "limit" option that defaults to -1. We can then get the quorum in GetValue and then apply it as the limit. Otherwise, we'll always stop after the default quorum (if not specified).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: by default, we should continue searching until we run out of peers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we may just want to make GetValue and SearchValue orthogonal (using the same GetValues under the covers).

routing.go Outdated
select {
case r, ok := <-responses:
if !ok {
responses = nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just use a break label.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would lead to occasionally ignoring errors

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. I didn't see the && below.

Actually, do we even need an error channel? I'd just return an error up front (valid key, non-empty routing table, etc.) and then close when done. At that point, I don't see how we can really "fail". If the user wants more context, they can register a notifier.

Is that possible? It'll be a lot nicer to the user.

routing.go Outdated
}

// GetValues gets nvals values corresponding to the given key.
func (dht *IpfsDHT) GetValues(ctx context.Context, key string, nvals int) (_ []RecvdVal, err error) {
func (dht *IpfsDHT) GetValues(ctx context.Context, key string, nvals int) <-chan RecvdVal {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may not want to change the API here. For now, I'd just add a private getValues function and make GetValue a simple wrapper.

@magik6k
Copy link
Contributor Author

magik6k commented Jul 18, 2018

On quorum on SearchValue - I think it makes more sense than a limit option. As implemented the option does pretty much exactly what it did before, which is basically 'ask that many peers before giving a final value'. A limit option would imply that we expect multiple versions of a key, which usually/optimally shouldn't be the case.

@magik6k magik6k changed the title [WIP] Implement SearchValue Implement SearchValue Jul 18, 2018
@Stebalien
Copy link
Member

On quorum on SearchValue - I think it makes more sense than a limit option. As implemented the option does pretty much exactly what it did before, which is basically 'ask that many peers before giving a final value'. A limit option would imply that we expect multiple versions of a key, which usually/optimally shouldn't be the case.

Yeah, you're probably right. However, I'd default to "continue till we run out of peers" instead of the default quorum of 16.

@magik6k
Copy link
Contributor Author

magik6k commented Jul 18, 2018

Yeah, you're probably right. However, I'd default to "continue till we run out of peers" instead of the default quorum of 16.

Done

ext_test.go Outdated
@@ -48,7 +47,7 @@ func TestGetFailures(t *testing.T) {
err = merr[0]
}

if err != io.EOF {
if err != routing.ErrNotFound {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a context.ErrDeadlineExceeded. We'll probably need to check if the context was canceled in GetValue when the channel is closed.

routing.go Outdated
if responsesNeeded < 0 {
responsesNeeded = 0
}
vals := make([]RecvdVal, 0, responsesNeeded)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could grow very large (with no quorum). We may want to fire off a round of corrections ocationally (i.e., see 30 values, correct peers and reset). However, we can probably leave that as a TODO for now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we could just cap responsesNeeded for now. Actually, that's probably the best thing to do. Cap it at 50 or something.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving it unbounded could also cause a bunch of goroutines below.

}
if len(recs) == 0 {

This comment was marked as resolved.

routing.go Outdated
if err != nil {
continue //TODO: Do we want to do something with the error here?
}
if i != best && !bytes.Equal(v.Val, vals[best].Val) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will be either 0 or 1, not best.

routing.go Outdated
if best > -1 {
i, err := dht.Validator.Select(key, [][]byte{vals[best].Val, v.Val})
if err != nil {
continue //TODO: Do we want to do something with the error here?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log an error. This should never happen. Honestly, we could just panic (but I'd rather not).

routing.go Outdated
out = append(out, val)
}

return out, nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to return this plus ctx.Error() (the context could have been canceled, etc.).

ext_test.go Outdated
record "github.com/libp2p/go-libp2p-record"
routing "github.com/libp2p/go-libp2p-routing"
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
"github.com/libp2p/go-libp2p-record"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we never really use consistent naming, in package names, I'd rather leave the explicit names in-place.

@@ -48,7 +48,7 @@ func TestGetFailures(t *testing.T) {
err = merr[0]
}

if err != io.EOF {
if err != context.DeadlineExceeded {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

routing.go Outdated
if len(vals) < maxVals {
vals = append(vals, v)
} else {
i = (best + 1) % maxVals
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i could still be out of bounds. It may be better to simply put the "best" value in a separate variable and then keep an array of peers to correct.

routing.go Outdated
return out, ctx.Err()
}

func (dht *IpfsDHT) getValues(ctx context.Context, key string, nvals int) (<-chan RecvdVal, error) {
eip := log.EventBegin(ctx, "GetValues")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably go into GetValues. That may also help simplify/get rid of the done dance.

routing.go Outdated
@@ -260,10 +349,11 @@ func (dht *IpfsDHT) GetValues(ctx context.Context, key string, nvals int) (_ []R
From: p,
}
valslock.Lock()
vals = append(vals, rv)
vals <- rv
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should select on the context.

Copy link
Member

@Stebalien Stebalien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed two blocking channel writes.

routing.go Outdated
}
if sel == 1 && !bytes.Equal(v.Val, best.Val) {
best = &v
out <- v.Val
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to select on the context.

routing.go Outdated
if err := dht.Validator.Validate(key, v.Val); err == nil {
best = &v
out <- v.Val
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

Copy link
Member

@Stebalien Stebalien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I have a few nits and cleanups but this should work. We can do that in a separate PR.

@ghost ghost assigned Stebalien Aug 10, 2018
@Stebalien Stebalien merged commit f6a03cb into master Aug 10, 2018
@ghost ghost removed the status/in-progress In progress label Aug 10, 2018
@Stebalien Stebalien deleted the feat/searchvalue branch August 10, 2018 20:55
@magik6k magik6k mentioned this pull request Aug 30, 2018
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants