Over the last couple days a number of people on our team have been investigating why an operation marked as one way on one of our WCF services ends up blocking when it's called. We looked at a number of things with our setup including:
- Making sure our operation did in fact have IsOneWay=true defined
- Ensuring none of the behaviors we have setup for exception sheilding and authorization were cuasing the call to block
- Ensuring that it wasn't something within our client by creating a console app to replicate the problem
- Validating the configuration on both the client and service side where both correct
- and more...
Yesterday afternoon
Sam and I began to dig into the problem a little deeper by turning on tracing and message logging for our services. After spending a bit of time tracing through the logs with SvcTraceViewer we eventually spotted something in the client side log that didn't look right. The entry looked something like this:
Close Secure Session 10s
What was mysterious about this line was that in order to replicate and debug the problem we had a service that looked like this:
[OperationContract(IsOneWay=true)]
public void MyOneWayOperation(ItemRequest request) {
Thread.Sleep(10000);
}
So it appeared that the Close on channel was actually blocking until our one way operation completed. We weren't totally convinced that this could really be the case, so we changed our operation like so:
[OperationContract(IsOneWay=true)]
public void MyOneWayOperation(ItemRequest request) {
Thread.Sleep(20000);
}
which resulted in the following line in our log:
Close Secure Session 20s
This convinced both of us that it was defintely blocking when closing down the client proxy. At one point in the past we had run one way messages and not seen this behavior though, so what had changed to all the sudden cause our operation with IsOneWay=true set to block? It turns out that the "problem" was due to a change in the way we used the client proxies. Previously we had code that looked like this:
MyServiceClient client = RetrieveClientProxy();
client.MyOneWayOperation(new ItemRequest("KLDF992"));
But due to some problems that we've had with secure sessions timing out, and other inconsisitencies we changed our code to:
using(MyServiceClient client = new MyServiceClient()) {
client.MyOneWayOperation(new ItemRequest("KLDF992"));
} // FYI, Dispose on a ClientBase calls Close
Instead of re-using our client proxies we're now recreating our client proxy every time an operation is called. This results in our performance not being as great as it could be since the client has to re-establish it's security context on every call, but, it's also resulted in our services being a lot more reliable. We'd rather have things run slightly slower than have them not work.
I still don't understand why closing the client proxy would block when calling a one way operation. Since a one way operation immediately returns a 202 letting the client know that it was received I'm unsure of why the proxy needs to wait around for the operation to finish when you call Close on it. It seems to defeat the whole purpose/nature of a one way operation. I've explicitly said that I don't care about the return value so I don't see why it can't just close when I tell it to.