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

More server API updates #88

Closed
wants to merge 18 commits into from
Closed

Conversation

johningve
Copy link
Member

@johningve johningve commented Jul 7, 2020

This PR changes the server API again...

Instead of using channels to send responses, we can use a simple func that embeds the message in metadata and forwards onto the correct channel. Thus, we no longer have to create a new goroutine for every single message.

This PR also adds the server stream context as a parameter to each handler, as well as new manager options for setting metadata. The metadata can be extracted from the stream context:

metadata.FromIncomingContext(ctx)

In addition, I have found a way to make the TestSetup helper useful for GorumsServer types.

The new server API:

func (srv *orderedServer) OrderedQC(_ context.Context, in *Echo, out func(*Echo)) {
	out(in)
}

Am leaving this as a draft for now, as one of the tests is failing. Will investigate tomorrow...

EDIT: failing ordering test was due to indexing QF map incorrectly.

Instead of sending replies from server handlers on a channel that is
then read by a separate goroutine, we can use a function to process the
reply and send it. This way we avoid creating unnecessary goroutines.
Also exposes the context of NodeStream in each handler.
This allows for some metadata to be accessed on the server side.
The metadata is only set once, when the client connects to the server.
TestSetup can now work with any server type that implements the Serve()
and Stop() functions.
By giving the port number '0', we can get an available port selected by
the system.
Since we're now using maps in QF, we can no longer simply return index 0
@johningve johningve marked this pull request as ready for review July 8, 2020 07:48
@johningve johningve requested a review from meling July 8, 2020 07:48
The server-side metadata allows us to check the auth type of the client.
This commit adds a test to see if TLS is working with Gorums.
Also, turns out that the reason the old TLS test broke was because the
included cert had expired. To avoid this problem in the future, the test
now generates its own certificate.
@johningve
Copy link
Member Author

@meling I added a new test for TLS. I think that the old test broke because the included certificate expired. To avoid that problem, the new test generates its own TLS certificate.

@johningve
Copy link
Member Author

@meling I've also added a test that verifies that reconnection of the streams is working. Let me know if you want me to break some of these changes out into separate pull requests.

@johningve
Copy link
Member Author

Also, should we consider not including all of the generated code for the tests, and rely on the Makefile instead? The amount of code that gets updated from a single change in gorums is quite large now. We could set up a Makefile target to run all of the tests and then set up a GitHub action to run the tests automatically.

// Package testing provide a public API for setting up Gorums.
// This package can be used by other packages, such as Raft and HotStuff.

// TestSetup starts numServers gRPC servers using the given registration
// function, and returns the server addresses along with a stop function
// that should be called to shut down the test.
func TestSetup(t testing.TB, numServers int, regSrvFn func(*grpc.Server)) ([]string, func()) {
func TestSetup(t testing.TB, numServers int, srvFunc func() interface{}) ([]string, func()) {
Copy link
Member

Choose a reason for hiding this comment

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

Since the TestSetup function only works srvFunc() that match the server interface, we could export the interface as gorums.Server. However, this creates an exported interface that is only used for this one test method. I think I would be happier if this TestSetup method and the Server interface were placed in a separate package, different from the gorums root package. I originally placed it in a package testing, but that caused naming problems with the std lib testing package. Right now, I'm not able to come up with a good name. Maybe it would be solved if we renamed the imported gorums/testing package to gt or something.
@Raytar What's your take?

Copy link
Member

Choose a reason for hiding this comment

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

On the other hand, I actually like calling gorums.TestSetup.

md, ok := metadata.FromIncomingContext(ctx)
if !ok {
srv.t.Error("Failed to obtain metadata!")
out(&NodeID{})
Copy link
Member

Choose a reason for hiding this comment

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

This pattern has me a bit worried. Normally, we would return an error to the client here, e.g.:

  return status.Errorf(codes.InvalidArgument, "Failed to obtain...")

Not sure how we should handle this. I was thinking that the error could be passed to the out func, but this won't work.

}

if resp.GetID() != node.ID() {
t.Fatalf("Wrong message")
Copy link
Member

Choose a reason for hiding this comment

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

Should follow the test pattern linked to from issue #90

t.Fatalf("Failed to execute RPC: %v", err)
}

if resp.GetID() != 1 {
Copy link
Member

Choose a reason for hiding this comment

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

Should follow test pattern in issue #90

finished <- &gorumsMessage{metadata: in.metadata, message: resp}
}()
srv.{{.GoName}}(req, c)
once := new({{use "sync.Once" $genFile}})
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a brief comment here explaining why we use the once.Do() thing?

Also, does the finished channel mean the queue of messages to be sent on the stream? I think here it sounds okay to use finished (maybe), but in the NodeStream implementation, it is also called finished. Maybe this should be called something else? (outQueue and sendQueue?)

@meling
Copy link
Member

meling commented Jul 9, 2020

Also, should we consider not including all of the generated code for the tests, and rely on the Makefile instead? The amount of code that gets updated from a single change in gorums is quite large now. We could set up a Makefile target to run all of the tests and then set up a GitHub action to run the tests automatically.

@Raytar Agree with this. I've been wanting to set up CI testing, as mentioned in #90. We should still keep the zorums* generated files in dev, since they are required to compile the static files.

Edit: I've started (a bit late, so won't finish now) to rewrite the stability checker in the Makefile. I'm invoking the protoc func from a TestGorumsStability (see below). I plan to do the same for the other gentest targets. That way, we don't need the tricky Makefile targets...

func TestGorumsStability(t *testing.T) {
	t.Log("Running protoc test with source files expected to remain stable (no output change between runs)")
	// Go 1.15 specific funcs:
	dir1 := t.TempDir()
	dir2 := t.TempDir()
	// cp zorums.proto dir1
	// cp zorums.proto dir2
	protoc("dir1/zorums.proto")
	protoc("dir2/zorums.proto")
	// diff dir1 dir2
	// return error if different; maybe use cmp package to show differences.
}

@meling
Copy link
Member

meling commented Jul 9, 2020

Thanks for adding tests and the TLS cert generation. Maybe it will be easier to keep multiple PRs, if we don't check in all the generated code. I guess we will still get conflicts in the zorums* files.

@johningve
Copy link
Member Author

Closing in favor of #91 and #92.

@johningve johningve closed this Jul 10, 2020
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