Skip to content

Commit

Permalink
Implement flow control to fix deadlock issues that can happen with st…
Browse files Browse the repository at this point in the history
…reaming RPCs (#11)

Note: this commit is _not_ compatible with v0.1.0. Users should first upgrade all
clients and servers to v0.2.0 before attempting to use this change.
  • Loading branch information
jhump authored Oct 23, 2023
1 parent 695f991 commit 56803ed
Show file tree
Hide file tree
Showing 16 changed files with 1,765 additions and 628 deletions.
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ vet:

.PHONY: staticcheck
staticcheck:
@go install honnef.co/go/tools/cmd/[email protected].0
@go install honnef.co/go/tools/cmd/[email protected].6
staticcheck ./...

.PHONY: ineffassign
Expand All @@ -53,7 +53,6 @@ test:

.PHONY: generate
generate:
cd proto && buf generate
go generate ./...

.PHONY: testcover
Expand Down
31 changes: 12 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ methods for forward and reverse tunneling.

A forward tunnel allows for all requests made on the tunnel to be directed
to the same server. With a typical gRPC client, connecting to a replicated
server, requests can round-robin across backends. For typical stateless
applications, this is desirable for load balancing and robustness. But some
applications that are not stateless may need affinity. The tunnel provides
that affinity. Instead of the client making multiple requests, which could
all be directed to different backends, the client makes one request to open
a tunnel. The resulting tunnel can then be used to create other RPC stubs,
so that all requests issued via those stubs are directed to the single
backend to which the tunnel was opened.
server, requests are typically load balanced across backends. For typical
stateless applications, this is desirable for resource utilization and fault
tolerance. But some applications that are not stateless may need affinity.
The tunnel provides that affinity. Instead of the client making multiple
requests, which could all be directed to different backends, the client
makes one request to open a tunnel. The resulting tunnel can then be used
to create other RPC stubs, so that all requests issued via those stubs are
directed to the single backend to which the tunnel was opened.

* **Reverse Tunnel**: A reverse tunnel is the opposite: requests flow in the
reverse direction of a normal gRPC connection. This means that the gRPC
Expand Down Expand Up @@ -145,27 +145,20 @@ if err != nil {
log.Fatal(err)
}

// Create the tunnel.
tunnelStub := tunnelpb.NewTunnelServiceClient(cc)
stream, err := tunnelStub.OpenTunnel(context.Background())

// Open a tunnel and return a channel.
ch, err := grpctunnel.NewChannel(tunnelStub)
// Opens a tunnel and return a channel.
ch, err := grpctunnel.NewChannel(tunnelStub).Start(context.Background())
if err != nil {
log.Fatal(err)
}

// TODO: Create stubs using ch to send RPCs through the tunnel.
```

Client code should not interact with the stream at all or risk corrupting the
tunneling protocol. (All interactions with the stream should be done via the
channel.)

To close the tunnel, use the channel's `Close` method. This will also close the
underlying stream. If any RPCs are in progress on the channel when it is closed,
they will be cancelled. The channel is also closed if the context used to create
the stream is cancelled or times out.
they will be cancelled. The channel is also closed if the context passed to
`Start` is cancelled or times out.

To use client interceptors with these channels, wrap them using
[`grpchan.InterceptClientConn`](https://pkg.go.dev/github.com/fullstorydev/grpchan#InterceptClientConn)
Expand Down
13 changes: 5 additions & 8 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,11 @@
// Forward tunnels allow a client to pin RPCs to a single server since they are
// all sent over a single stream. Forward tunnels work like so:
//
// - Client issues an RPC that establishes the forward tunnel. The RPC is a
// full-duplex bidirectional stream, so can support all manner of streaming
// RPCs over the tunnel.
// - Client then uses the tunnel to create a new gRPC client connection. (See
// NewChannel).
// - RPC stubs can then be created using this new connection. All RPCs issued
// on this connection are transmitted over the tunnel, on the stream that was
// established in step 1.
// - Client creates a new tunnel by calling NewChannel. This issues an RPC that
// establishes the forward tunnel. The RPC is a full-duplex bidirectional
// stream, so can support all manner of streaming RPCs over the tunnel.
// - RPC stubs can then be created using this tunnel. All RPCs issued on it are
// transmitted over the stream that was established by the RPC mentioned above.
// - Closing the tunnel channel also results in the underlying stream
// being closed.
//
Expand Down
Loading

0 comments on commit 56803ed

Please sign in to comment.