Go-plugin concurrency

Hi,
Just amazing products and company, an inspiration for the rest of us!

So, I’m sorry to put this question here but the go-plugin system does not look to have its own category :slight_smile:

I’m building an application where i load 2 different types of plugins, similar to what Terraform does… a provider and a resource-driver-plugin. The application has a API endpoint and each incoming request is put in channel where N workers grab that payload.

The worker first uses the provider plugins to do a lookup (the plugin will have a database connection or make a northbound api etc) after this is done a resource plugin is selected and the resource plugin will send N request to north-bound system and get a ID in return. the Workers will also listen from complete jobs that the northbound application publish in a queue and use the plugin to parse the payload to a uniform output that returns to the caller of the API.

This would make the N workers grab a plugin from the pluginMap (the pluginMap is protected by a mutex to avoid race to the pluginMap) and it will execute the functions requested from the southbound API. SO!

I have understod that your yamux lib is creating new tcp connections over a single tcp connection. would this work in my use case? or should i let each worker initiate its own pluginMap ?

Thanks,
Mathias

Hey Mathias,

You should be able to use a global plugin map for all of the workers, the plugin map stores a global reference to a client and should not require any sync.Mutex as you are only obtaining a reference.

The client itself supports multiple concurrent requests, for example in the instance of the example in the go plugin repo (https://github.com/hashicorp/go-plugin/blob/master/examples/grpc/shared/interface.go). The client Put or Get command is a wrapper around an RPC or gRPC call to the plugin implementation.

This is essentially the same as the communication between two services over a network, the difference with GoPlugin is that you are running the server as a binary along with your application and should support multiple concurrent requests.

A comparable pattern in Go is http.Client, the client is generally a global object and is used by multiple go routines concurrently. If two go routines simultaneously attempt to make a HTTP request, the client allocates a connection to each request and processes them concurrently.

Go plugin supports the same pattern so it is safe to share a client among multiple Goroutines without having to worry about thread safety.

The key thing you might need to consider is in the server implementation of the plugin, if this can not support multiple simultaneous requests then you may have to implement some form of locking to ensure that subsequent requests are blocked until the first completes. From your intended use case of making a Database call or Upstream API call I think you do not need to worry about this as both up-streams should be able to handle simultaneous connections.

Hope this helps and please ping back here if there is anything I can clarify further.

Nic

2 Likes

In addition to all of Nic’s great advice, I just wanted to add: go-plugin currently supports both net/rpc and gRPC transports, and we are generally considering the net/rpc transport to be a legacy system for backward-compatibility. I’d suggest for any new application to use go-plugin in gRPC mode, which has the following differences and benefits:

  • gRPC supports many different languages for plugin implementation in principle, while net/rpc is Go-specific. (In practice, only Go has a complete implementation of the server side of the plugin protocol still, but it’s relatively straightforward to build another because it’s just a thin wrapper around a gRPC server to handle the initialization handshake.)
  • gRPC mode sends requests over HTTP2 channels rather than using yamux.
  • gRPC uses an external protobuf definition to define the plugin API, while net/rpc mode using native Go types. While using native Go types is convenient, we’ve found that in practice it tends to create strong coupling between the plugin protocol and the calling application and thus makes it hard to evolve the protocol in future while retaining compatibility with existing plugins. (That is why Terraform 0.12 has a completely incompatible plugin protocol: we’d reached the limits to how far we could grow the old protocol without breaking wire compatibility, and so we took this opportunity to switch to gRPC so we can use protobuf’s forward- and backward-compatibility features in future changes.)
  • Each plugin process contains only one “instance” of the plugin in gRPC mode, whereas in net/rpc mode the client can potentially instantiate multiple instances in a single process. (If your application would benefit from multiple plugin instances in a single process then this may be a downside for you, but seems to have been a moot point for Terraform so far.)

Thanks for the good answers. It seems like go-plugin will be able to do what I need it to!

Thanks, I choose the regular RPC as I believe the development is slightly faster (not adding protobuf into the mix). But looking at your answer it might be better to implement it right away. :slight_smile: