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

Split P2P Topics And Introduce Middleware (Adapters) #421

Merged
merged 32 commits into from
Aug 29, 2018

Conversation

prestonvanloon
Copy link
Member

Resolves #363

This is still a bit of work in progress as this feature is lacking some critical test paths.
I'm opening this PR for exposure and tracking this work in progress.

New features:

  • Beacon chain and sharding client subscribe to their specific topics only.
  • A middleware adapter pattern enables some p2p middleware layer functionality such as streamz metrics counting (via prometheus), instrumentation (via opentracing), request rate limiting, debug logging, etc.

The above middleware examples are not added in this PR but are likely candidates for new features.

@prestonvanloon prestonvanloon added this to the Ruby milestone Aug 19, 2018
@prestonvanloon prestonvanloon self-assigned this Aug 19, 2018
@codecov
Copy link

codecov bot commented Aug 19, 2018

Codecov Report

Merging #421 into master will decrease coverage by 0.12%.
The diff coverage is 83.78%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #421      +/-   ##
==========================================
- Coverage   76.58%   76.45%   -0.13%     
==========================================
  Files          35       36       +1     
  Lines        2242     2251       +9     
==========================================
+ Hits         1717     1721       +4     
- Misses        368      372       +4     
- Partials      157      158       +1
Impacted Files Coverage Δ
beacon-chain/node/node.go 58.97% <100%> (ø) ⬆️
shared/p2p/discovery.go 88.23% <100%> (-4.08%) ⬇️
validator/node/node.go 40.21% <100%> (ø) ⬆️
beacon-chain/node/p2p_config.go 75% <75%> (ø)
validator/node/p2p_config.go 75% <75%> (ø)
shared/p2p/service.go 71.27% <85.18%> (+13.95%) ⬆️
beacon-chain/rpc/service.go 94.59% <0%> (-5.41%) ⬇️
beacon-chain/sync/initial-sync/service.go 68.33% <0%> (-4.17%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5adc94b...e13aa0c. Read the comment docs.

@rauljordan
Copy link
Contributor

Gazelle needs rerun but the middleware idea is awesome! Curious to see if perhaps a p2p incentive layer could be attached as something of that sort.

@prestonvanloon
Copy link
Member Author

This is ready for review. Please look

return nil, err
}

// TODO: Define default adapters for logging, monitoring, etc.
Copy link
Member

Choose a reason for hiding this comment

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

Can we open an issue for this?


s, _ := p2p.NewServer()

// TODO: Figure out the topic. Is it a protobuf topic, string, or int?
Copy link
Member

Choose a reason for hiding this comment

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

We should create an issue for this too for new comers to pick it up

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, this is a leftover TODO. It's a string at the moment.

Copy link
Contributor

@rawfalafel rawfalafel left a comment

Choose a reason for hiding this comment

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

Looking good. My main concern is that we've been passing around messages as an interface{} type. Would be nice if we can clean that up in this PR. Otherwise, the actual splitting of topics looks 👌

}

// Reverse adapter order
for i := len(adapters)/2 - 1; i >= 0; i-- {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this list be reversed outside of the loop, or can this list change over time?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, good catch!

return
}

i := feed.Send(Message{Data: d})
Copy link
Contributor

Choose a reason for hiding this comment

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

Here's the definition for Message:

type Message struct {
	// Peer represents the sender of the message.
	Peer Peer
	// Data can be any type of message found in sharding/p2p/proto package.
	Data interface{}
}

Can type interface{} type for Data be replaced with proto.Message? Allowing any type here seems unnecessarily lax, especially considering that the value was cast down to proto.Message at the beginning of this function.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good to me

Copy link
Member

@nisdas nisdas left a comment

Choose a reason for hiding this comment

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

Everything looks great! Just had a few comments. Also is it possible to get the number of peers connected ? We could use it for sync

// limiter or blacklisting condition.
func reqLogger(next p2p.Handler) p2p.Handler {
return func(ctx context.Context, msg p2p.Message) {
fmt.Printf("Received message from %s\n", msg.Peer)
Copy link
Member

Choose a reason for hiding this comment

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

msg.Peer should be %v


s.RegisterTopic(topic, message, adapters...)

ch := make(chan p2p.Message)
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be buffered ?

Copy link
Contributor

@rawfalafel rawfalafel left a comment

Choose a reason for hiding this comment

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

We should definitely avoid using arbitrary timeouts in tests. I poked around libp2p to see if there was a workaround, but nothing obvious popped up.

I'm also curoius if subscribeToTopic is necessary at all, now that we have RegisterTopic.

//
// The topics can originate from multiple sources. In other words, messages on
// TopicA may come from direct peer communication or a pub/sub channel.
func (s *Server) RegisterTopic(topic string, message interface{}, adapters ...Adapter) {
Copy link
Contributor

Choose a reason for hiding this comment

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

message interface{} can be proto.Message as well

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried this and it's failing tests. I wonder if reflect is assuming that TypeOf(message) is proto.Message rather than the full proto definition.

I didn't investigate thoroughly.

}

func (s *Server) emit(feed *event.Feed, msg *floodsub.Message, msgType reflect.Type) {
// TODO: reflect.Value.Interface() can panic so we should capture that
Copy link
Contributor

Choose a reason for hiding this comment

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

You shouldn't have to handle this case explictly, since we're never passing in unexported fields for this value.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, I dont recall why the TODO was here so I will remove it. Hopefully it doesn't cause issues.

h = adapter(h)
}

ctx, _ := context.WithTimeout(s.ctx, 10*time.Second)
Copy link
Contributor

Choose a reason for hiding this comment

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

cancel should be called at the end of this loop to avoid memory leaks

Copy link
Member Author

Choose a reason for hiding this comment

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

Why? There is a timeout, i'm not sure we even need this timeout. I'll just remove it.

Copy link
Contributor

Choose a reason for hiding this comment

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

https://stackoverflow.com/questions/44393995/what-happens-if-i-dont-cancel-a-context

Looks like the timeout exists so that the adapter doesn't run continuously for more than 10 seconds.

log.WithFields(logrus.Fields{
"topic": topic,
}).Debug("Subscribing to topic")
go s.subscribeToTopic(topic, msgType)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we remove this method, subscribeToTopic?

Copy link
Member Author

Choose a reason for hiding this comment

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

I am surprised this didn't upset the unused linter. It was only used in tests!

wait := make(chan struct{})
go (func() {
defer close(wait)
msg := <-ch
Copy link
Contributor

Choose a reason for hiding this comment

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

You can replace this line and the next with just <-ch

}

// Short timeout to allow libp2p to handle peer connection.
time.Sleep(time.Millisecond * 10)
Copy link
Contributor

Choose a reason for hiding this comment

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

The description for BasicHost.Connect is:

// Connect ensures there is a connection between this host and the peer with
// given peer.ID. If there is not an active connection, Connect will issue a
// h.Network.Dial, and block until a connection is open, or an error is returned.
// Connect will absorb the addresses in pi into its internal peerstore.
// It will also resolve any /dns4, /dns6, and /dnsaddr addresses.

which suggests that this timeout shouldn't be necessary. Do you know why it's required? This is a potential source of flakiness, we should definitely try and avoid an arbitrary timeout.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not really. This pattern is all over the floodsub tests: https://github.com/libp2p/go-floodsub/blob/HEAD/floodsub_test.go

And it fails without it.

wait := make(chan struct{})
go (func() {
defer close(wait)
msg := <-ch
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

adapters[i], adapters[opp] = adapters[opp], adapters[i]
}

go (func() {
Copy link
Contributor

Choose a reason for hiding this comment

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

no need for go (func, do go func() {}() instead

We specify messages available for p2p communication common to beacon chain nodes and sharding clients.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a few sections here about the p2p middleware stuff for visibility?

Copy link
Member Author

Choose a reason for hiding this comment

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

What does this have to do with protos?

Copy link
Member

@terencechain terencechain left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@nisdas nisdas left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link
Contributor

@rawfalafel rawfalafel left a comment

Choose a reason for hiding this comment

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

LGTM

@rauljordan rauljordan merged commit b02042d into prysmaticlabs:master Aug 29, 2018
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.

5 participants