I built a windows forms application, which sends data (using a POST call) to a web service on a button click event. In production, sometimes the users are seeing the "Server Busy" message box (as below).
I am using Async Await pattern to post messages to the service. Below is my code snippet:
private void button1_Click(object sender, EventArgs e) { SendDataToHost(); } private async void SendDataToHost() { //Get the data which needs to be sent to the host.. var result= await PostData(data); //If the service fails to process this data for any reason, then // it returns the result object with succeeded as false if (result != null && !result.Succeeded) { MessageBox.Show("Failed"); } else { MessageBox.Show("Success"); } } public async Task<Result> PostData(Data data) { Result result = await PostAsync("cases", data); return result; }
I understand the "Server busy" message box is being populated because the UI thread is being blocked. But what I am trying to understand is, the purpose of using Async and Await is to keep the UI responsive, but why is the UI thread blocked?
Also, I researched online and I found some solutions, but I want to completely understand if they will fix the issue? As it is a production issue and I am not able to reproduce in dev environment and I want to be sure that the change will fix the issue.
The 2 options I found are:
1) To use ConfigureWait(false). From my understanding, this will make sure the "POST" call does not happen in UI context.
public async Task<Result> PostData(Data data) { Result result = await PostAsync("cases",data).ConfigureAwait(false); return result; }
2) Call the Post Method in a new Task and await on the Task.
private async void SendDataToHost() { //Get the data which needs to be sent to the host.. **var result= await Task.Run(()=>PostData(data));** //If the service fails to process this data for any reason, then // it returns the result object with succeeded as false if (result != null && !result.Succeeded) { MessageBox.Show("Failed"); } else { MessageBox.Show("Success"); } }
I would like to know if I am heading in the right direction to solve this issue or are there any other options I need to explore?
1 Answers
Answers 1
It is a very standard message box, many programs can display it. Here is an example of QuickBooks doing it, googling "server busy this action cannot be completed" will give you many more examples of it. The advice they give to solve it is rarely very useful.
Visual Studio is a good example of an app that has this feature as well. It however doesn't use the standard message box but customized the way it reports the mishap. Check this Q+A.
There is not enough of a cue in your question to guess what "server" might be and why it is misbehaving. It certainly is not a "web service", somebody is selling you a load of horse feathers, not very uncommon for a very old code base that nobody really understands anymore. So I'll have to tackle this by explaining the plumbing. The message box is displayed by the COM infrastructure built into the OS. It gets involved in your program when you make a call on a COM server and that call needs to be marshaled to another thread or another process. PostAsync() presumably is that call, albeit that it doesn't have a very typical name for such a method. Maybe it was wrapped by somebody to make it async.
One service provided by the COM infrastructure is that it monitors such a call. Ensuring that it completes in a reasonable amount of time. Where "reasonable" is 60 seconds, about as long as a human is willing to wait when a program becomes unresponsive. Having such calls take too long is a pretty severe usability problem, it has a knack for making the user interface of your program hang and for the user losing control over the program.
The intention of the message box is to let him know that something is a-miss and that it isn't your program that has the problem. The "Switch To" button however rarely has the intended effect, not anymore. In the olden days when OLE was still popular, and this mishap was far more common, it would help the user to click on a message box or otherwise get a diagnostic from the server to find out why it is not co-operating.
Today, the far more likely cause is a fire-hose problem. Pummeling the server with requests at a rate far higher than it can execute. This is for one the likely reason you are pursuing async code, you probably noticed before that the server wasn't very quick to do its job. Which is okay if that made your UI unresponsive, but it doesn't solve the core problem. And it in fact can make it a lot worse, your program can now make these calls a lot quicker, thus making the firehose problem worse. And ultimately triggering the COM diagnostic when the server gets backed-up too much.
There are no clean solutions to this, you just have to go slower so your program doesn't overwhelm the server. It is technically possible to suppress the message box so it never appears, usually what programmers look for first. You can do so by writing your own IMessageFilter interface implementation and tell the COM plumbing about it by pinvoking CoRegisterMessageFilter(). You'll find the boilerplate C# code in this MSDN article.
Do note that the Register() call must be made by the same thread that created the COM object, this is not necessarily simple if the component was wrapped or when you are creating it on a non-STA worker thread. You'll find code that creates a hospitable home for such a component, so you can reliably make the Register() call in this post. Also gives you a way to create your own queue and avoid the async calls so you don't depend on the COM plumbing.
0 comments:
Post a Comment