Background
The service broker framework's GetProxyAsync calls currently return either a local proxy of its own making or an RPC proxy from StreamJsonRpc, depending on whether the brokered service and its client require RPC.
The RPC proxy can die if the RPC connection fails. It can also become stale when the proffering party disposes its proffered factory (and possibly proffers a new one).
Today, the ServiceBrokerClient tries to help brokered service clients remain ignorant of these changes by producing a proxy whenever they want it, caching proxies between requests and refreshing them as needed automatically. It doesn't work very well with event handlers though, and the user that adds event handlers to proxies has to advise to an event from the ServiceBrokerClient so it knows when to re-apply event handlers to a refreshed proxy.
The problem
This pattern requires that classes that need a brokered service take an IServiceBroker or ServiceBrokerClient as a constructor parameter, then acquire the proxy themselves as needed. This 'hides' from users of the class which brokered services will actually be required, making writing test clients of the class more complicated because it's not clear which mocks must be created.
Passing in the IFooService proxy directly to the class would be better for discoverability of what services are required. It would also simplify the class implementation since it doesn't have to know that it's a brokered service or deal with GetProxyAsync calls. But this pattern leaves the class vulnerable to RPC failure where an RPC proxy would be zombied and the class couldn't refresh it. It also wouldn't handle refreshing the proxy in the event of a proferring factory change.
The proposed solution
Introduce a new proxy implementation. We'll call it the "resilient proxy". It can be procured once and internally behaves something like ServiceBrokerClient in that it will refresh its connection to the brokered service as-needed. In this way, a class that consumes an IFooService can just take that as a parameter, and expect it will keep working. Tests can provide a mock directly without mocking up an IServiceBroker. Real clients can procure a resilient proxy and provide that to the class upon construction.
This resilient proxy will wrap RPC proxies, and maybe local proxies too.
This new proxy implementation should be available via source generation and perhaps Ref.Emit.
Open questions
- Would this resilient proxy support both local and RPC brokered services?
- The legacy proxy implementations include special interfaces that can be useful for some clients. Will these be exposed through the resilient proxy? How? The set of interfaces vary between local and RPC proxies. It could be through an interface of its own that exposes a property whose value is the inner-proxy.
- Will this proxy be supported via Ref.Emit, or only source generation?
- Will this become the default proxy type, or will the caller have to ask for it specifically somehow (probably through a service broker aggregator/wrapper)?
- How will event handlers be supported? Or more generally, how will clients know when the service behind the proxy has been refreshed such that the mutable state that the client may have built up in that instance of the service is understood to be gone?
Background
The service broker framework's
GetProxyAsynccalls currently return either a local proxy of its own making or an RPC proxy from StreamJsonRpc, depending on whether the brokered service and its client require RPC.The RPC proxy can die if the RPC connection fails. It can also become stale when the proffering party disposes its proffered factory (and possibly proffers a new one).
Today, the
ServiceBrokerClienttries to help brokered service clients remain ignorant of these changes by producing a proxy whenever they want it, caching proxies between requests and refreshing them as needed automatically. It doesn't work very well with event handlers though, and the user that adds event handlers to proxies has to advise to an event from theServiceBrokerClientso it knows when to re-apply event handlers to a refreshed proxy.The problem
This pattern requires that classes that need a brokered service take an
IServiceBrokerorServiceBrokerClientas a constructor parameter, then acquire the proxy themselves as needed. This 'hides' from users of the class which brokered services will actually be required, making writing test clients of the class more complicated because it's not clear which mocks must be created.Passing in the
IFooServiceproxy directly to the class would be better for discoverability of what services are required. It would also simplify the class implementation since it doesn't have to know that it's a brokered service or deal with GetProxyAsync calls. But this pattern leaves the class vulnerable to RPC failure where an RPC proxy would be zombied and the class couldn't refresh it. It also wouldn't handle refreshing the proxy in the event of a proferring factory change.The proposed solution
Introduce a new proxy implementation. We'll call it the "resilient proxy". It can be procured once and internally behaves something like
ServiceBrokerClientin that it will refresh its connection to the brokered service as-needed. In this way, a class that consumes anIFooServicecan just take that as a parameter, and expect it will keep working. Tests can provide a mock directly without mocking up anIServiceBroker. Real clients can procure a resilient proxy and provide that to the class upon construction.This resilient proxy will wrap RPC proxies, and maybe local proxies too.
This new proxy implementation should be available via source generation and perhaps Ref.Emit.
Open questions