Wednesday, December 05, 2007

Why Web Services have failed me.

So I have an assignment where I am to explore the possibility of creating a web service that can report its progress of a task back to the client.

I can see you web developers rolling your eyes now. I understand. The assignment came from a non-web programmer, so he didn't see the immediate problem: web services are state-less. What that means is that each request from the client is complete. The server doesn't need to know what was requested 10 requests ago. The server doesn't even need to know what the client is doing right now. The client says, "Server, do this." The server does it and sends the response. Then the server quickly forgets that the client ever existed, or what it even did for the client.

Now ASP.NET tries to get around a lot of this with a Session object (Session isn't specific to ASP.NET, but that's the language I am working with). When you have Session enabled on your web application, the server gives each client a GUID (Global Unique Identifier). As long as the client supports sessions also (normally by supporting cookies), the client will send that GUID with every request it makes to the server.

Now there are a whole host of reasons why you would not want to enable session in a web service, but people do it, and you are just going to have to decide for yourself whether the pros out weigh the cons. In this case (since it was just a prototype) I decided I needed to know if it was possible, not if it worked well.

So I created a web service that enabled session. I then created two methods, one called DoSomethingForALongTime(), and another called GetProgress(). They looked like this:


int DoSomeThingForALongTime()
{
  for (int i = 1; i <= 4; i++)
  {
    Session["Progress"] = i*25; // Update the progress (25%, 50%, 75%, 100%).
    System.Threading.Thread.Sleep(6000); // Sleep for 6 seconds.
  }

  return(0); // Finished.
}

int GetProgress()
{
  return(Convert.ToInt32(Session["Progress"]));
}



So, my theory was that I could call DoSomethingForALongTime() asynchronously, and while that was executing call GetProgress() every two seconds and see what the progress was.

Well, it didn't work. Calling the methods worked fine, the issue was that the Session variable wasn't getting updated correctly. So after a couple of hours verifying the session variable updating was the issue, I went on Google and read up on Sessions in .Net.

It would seem that the great minds at Microsoft have elected to make the Session object thread-safe (kudos to ya!). That is actually a really great thing to do. So what ends up happening is that when my for loop writes the value to the Session["Progress"] variable, it doesn't actually write the value to Session yet. That value isn't written until DoSomethingForALongTime() exits. The assumption is that while one method is updating the Session object, you don't want to be able to read the new value from the Session object until the method that is updating it is completely done.

So I'm at a loss on how to fix this. The experiment was great because it forced me to learn a good deal about the intricacies of the Session object, but I would still really like a solution to my problem. I can see others getting the progress of the transmission of the response from the server, but I really need a way for the server to tell the client that the task is still executing, and how far along it is in completing the task.