Skip to content

Implement SearchValue#174

Merged
Stebalien merged 11 commits intomasterfrom
feat/searchvalue
Aug 10, 2018
Merged

Implement SearchValue#174
Stebalien merged 11 commits intomasterfrom
feat/searchvalue

Conversation

@magik6k
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor Author

magik6k commented Jul 18, 2018

Should be ready

Comment thread routing.go Outdated
return nil, routing.ErrNotFound
responsesNeeded := 0
if !cfg.Offline {
responsesNeeded = getQuorum(&cfg)
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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).

Comment thread routing.go Outdated
select {
case r, ok := <-responses:
if !ok {
responses = nil
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Comment thread 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
Copy Markdown
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 magik6k force-pushed the feat/searchvalue branch from 4ca5152 to 09f5251 Compare July 18, 2018 17:34
@magik6k
Copy link
Copy Markdown
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
Copy Markdown
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 magik6k force-pushed the feat/searchvalue branch from 790b5ca to f02c32e Compare July 18, 2018 18:26
@magik6k
Copy link
Copy Markdown
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

Comment thread ext_test.go Outdated
}

if err != io.EOF {
if err != routing.ErrNotFound {
Copy link
Copy Markdown
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.

Comment thread routing.go Outdated
if responsesNeeded < 0 {
responsesNeeded = 0
}
vals := make([]RecvdVal, 0, responsesNeeded)
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Comment thread routing.go
best = r
}
if len(recs) == 0 {

This comment was marked as resolved.

Comment thread 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
Copy Markdown
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.

Comment thread 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
Copy Markdown
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).

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

return out, nil
Copy link
Copy Markdown
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.).

@magik6k magik6k requested a review from Stebalien August 3, 2018 21:03
Comment thread 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
Copy Markdown
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.

Comment thread ext_test.go
}

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

Choose a reason for hiding this comment

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

❤️

Comment thread routing.go Outdated
if len(vals) < maxVals {
vals = append(vals, v)
} else {
i = (best + 1) % maxVals
Copy link
Copy Markdown
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.

Comment thread routing.go Outdated
}

func (dht *IpfsDHT) getValues(ctx context.Context, key string, nvals int) (<-chan RecvdVal, error) {
eip := log.EventBegin(ctx, "GetValues")
Copy link
Copy Markdown
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.

Comment thread routing.go Outdated
}
valslock.Lock()
vals = append(vals, rv)
vals <- rv
Copy link
Copy Markdown
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
Copy Markdown
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.

Comment thread routing.go Outdated
}
if sel == 1 && !bytes.Equal(v.Val, best.Val) {
best = &v
out <- v.Val
Copy link
Copy Markdown
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.

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

Choose a reason for hiding this comment

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

Same.

Copy link
Copy Markdown
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