This is a design question about the optimal/recommended way of maintaining shared mutable state using Kotlin concurrency primitives. For that, I need to establish the context first, please bear with me.
I’ve a Spring Boot web application that has a gRPC client. Following are the requirements/constraints on the system:
- Messages from the gRPC server could be informational or commands.
- The client has internal state, that may change depending on the commands received from the server. Informational messages don’t affect client state.
- Based on the commands received from the server, the client may generate more messages for the server, or update its internal state, or do both.
- Every client message generated due to a server command must happen after any state changes.
My current design is as follows;
- The client is a Spring bean. It has a reference to its internal state, which is a POJO (or POKO, if you want )
- Upon receiving a server message, it constructs a command instance and passes the message content and the client state to the command. This happens in a gRPC streaming receiver, which runs on a gRPC thread pool (default).
- The command instance may update the client state, and returns one or messages to the client.
- The client, running within an
Unconfineddispatcher, puts the messages in a buffered
Channel, and waits for the next server message.
Channelconsumer, running on Kotlin default dispatcher pool, picks up the messages and sends them to the server.
Notice that requirement #4 is satisfied by design steps #3 and #5.
- I keep hearing that
Flowis recommended over
Channel. In this case though, the
Channelneeds to stay open throughout the lifecycle of the client, and is very easy to use as an async queue. What benefits, if any, are there for using a
Flowin this case?
- I don’t feel particularly good about passing the client state to the commands. Is there a different way of achieving requirement #4 without doing this? (actors, may be)