-
Notifications
You must be signed in to change notification settings - Fork 14
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
Conversation
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
14288a9
to
5794e13
Compare
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.
@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. |
Changed some values to make it similar to the certs generated by https://golang.org/src/crypto/tls/generate_cert.go
@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. |
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()) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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{}) |
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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}}) |
There was a problem hiding this comment.
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?)
@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 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.
} |
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. |
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:
In addition, I have found a way to make the
TestSetup
helper useful forGorumsServer
types.The new server API:
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.