Skip to content
This repository has been archived by the owner on Aug 2, 2021. It is now read-only.

p2p/simulation: Test snapshot correctness and minimal benchmark #1038

Closed
wants to merge 13 commits into from

Conversation

nolash
Copy link
Contributor

@nolash nolash commented Dec 2, 2018

Pending merge of ethereum/go-ethereum#18220


This PR is part one of four PRs that adds test and benchmark to ensure that the snapshot connection content is correct upon creation, and that the correct connections are made after snapshot is loaded. All actual connections should be contained in snapshot, and all connections and only those connections should be in the network after load.

  1. Part one creates the correctness test and adds a vanilla benchmark function.

  2. Part two will move connection methods from swarm/network/simulation to p2p/simulations

  3. Part three will add a snapshot generation binary leveraging the different configurations provided by part two.

  4. Part four will add exhaustive benchmarks with different snapshots generated from part three.


This PR also moved the NoopService from the swarm/network/simulation package, since this minimal service is also useful for tests involving p2p/simulations directly.

It also adds a "test" tag to the travis script, to enable build of symbols across packages needed for testing only. (linter script will apparently not easily work with build tags)

// but implements node.Service interface.
type NoopService struct {
useChannel bool
C map[enode.ID]chan struct{}
Copy link
Member

Choose a reason for hiding this comment

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

C should not be exported. It is used only internally.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, it's used in network_test.go. And the idea is that this should be available outside of the package, which means it would need to be exported. But you mean that as long as that's not the case it should be private?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, when there is a need for it to be exported, then it should be exported, not before. Or if you want to provide the functionality from start, It would be good to have comments or examples how it should be used, as in network_test.go it is injected as the exported field.

I would even say that maybe useChannel is not needed, as NewNoopService can accept the map and set it to the field c so the Run function can check if the c map is nil instead to check if useChannel is true. This way, c can remain unexported with the functionality is available outside.

}

type noopService struct {
simulations.NoopService
Copy link
Member

Choose a reason for hiding this comment

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

While reducing code duplication, which is good, this approach also changes the previous noopService to have protocols, even more not to initialize a map used in the protocols run function. While this is working now, changes to the simulations.NoopService Run function may result in an unexpected failure in simulation package. This is creating more coupled code at the benefit of reducing a few lines in simulations package. I do not think that that benefit is great enough for this coupling, but I do not insist for changes, just awareness.

Copy link
Contributor Author

@nolash nolash Dec 3, 2018

Choose a reason for hiding this comment

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

How about a bool in the constructor that omits the protocol altogether, or is that too convoluted?

Alternately, chaining methods to add protocol or not, api or not...?

Copy link
Member

Choose a reason for hiding this comment

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

I think that any more options would just add to the complexity. I would like to avoid any more complexity. This is fine now, until it does not extended to be more convoluted.


// load the snapshot
// spawn separate thread to avoid deadlock in the event listeners
go func() {
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 signal to the main goroutine when the Load has returned to check if all connections are created before or after. They all should be created before when the ethereum/go-ethereum#18220 is merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool, let's mark this PR pending yours. That makes sense anyhow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually since the subscription is before, this is ok and even should be in a goroutine because we want to handle all the events as they happen, no?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, this should be in a goroutine, but this goroutine should signal the main goroutine when Load function returns, to check if that happens after all expected connection events are received. Or there are connections from the snapshot that happened after the Load function has returned, which should be an error.

Copy link
Contributor Author

@nolash nolash Dec 3, 2018

Choose a reason for hiding this comment

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

I understand what you mean. But given that we know exactly how many connections to expect, and we audit exactly those connections, and check at the end of the function for one second of "silence," isn't that already guaranteed?

(the latter would have to be checked in any case?)

Copy link
Member

Choose a reason for hiding this comment

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

The main purpose for ethereum/go-ethereum#18220 is that connections are all established before the Load function returns. This test currently do not provide such validation, as Load function is in the goroutine that can finish at any time and connection checking is in another goroutine that can finish at any time. Connections are checked but they can be established after the Load function return and this test does not check for that as the time when Load function returns is not known or such event is not signaled to the main goroutine.

p2p/simulations/network_test.go Show resolved Hide resolved
@zelig
Copy link
Member

zelig commented Dec 7, 2018

}

// check that we have all expected connections in the network
for _, snapConn := range snap.Conns {
Copy link
Contributor Author

@nolash nolash Dec 10, 2018

Choose a reason for hiding this comment

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

@janos since this is now a separate goroutine, can we be sure all of this will be executed before the test ends? The main routine may finish first. Or do tests in all cases join all threads before finishing?

Copy link
Member

Choose a reason for hiding this comment

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

Great catch @nolash. No implicit joining happens. I updated the code.

@nolash
Copy link
Contributor Author

nolash commented Dec 10, 2018

LGTM. Need one more review, I guess. Maybe even two, since we're now both complicit, @janos

@janos
Copy link
Member

janos commented Dec 10, 2018

Thanks @nolash. I agree. Since I also edited this PR, my approval should not count, but it LGTM.

Copy link
Member

@zelig zelig left a comment

Choose a reason for hiding this comment

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

LGTM, rebase and submit to upstream

params/config.go Outdated Show resolved Hide resolved
Copy link
Member

@acud acud left a comment

Choose a reason for hiding this comment

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

@nolash generally LGTM

})
// \todo consider making a member of network, set to true threadsafe when shutdown
runningOne := true
defer func() {
Copy link
Member

Choose a reason for hiding this comment

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

use sync.Once instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

well, I'm not sure it's clearer in this case. It would if both shutdowns were the same.

p2p/simulations/network_test.go Show resolved Hide resolved
p2p/simulations/network_test.go Show resolved Hide resolved
@nolash
Copy link
Contributor Author

nolash commented Dec 11, 2018

submitted upstream ethereum/go-ethereum#18287

frncmx pushed a commit that referenced this pull request Dec 13, 2018
This refactor was done as the second part of our current network
testing efforts; as outlined in #1038.

Co-authored-by: Elad Nachmias <theman@elad.im>
@zelig
Copy link
Member

zelig commented Dec 18, 2018

merged as ethereum/go-ethereum#18287

@zelig zelig closed this Dec 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants