Using a Singleton within a web service

G

Giulio Petrucci

Hi there,

I'm facing a weird problem in these days. First of all, let me describe
my scenario. I have a client application asking for some services to be
performed by a remote service, using a web service interface. Performing
such operations might take a long time, so the client application does
some "polling", asiking the server for the remote service status and
"consuming" partial results. Taking a look to the code-behin of my web
service, I have a ProcessAgent.cs class, which implements the process
itself. All the process agent instances are grouped within a
ProcessAgentPool which is a *singleton*. I made it as a singleton in
order to have a *unique* instance (to avoid the web service being
stateless ;-) ). The ProcessAgent class looks like (only declarations):

public class ProcessAgent
{
internal ProcessAgent(); //.ctor();
public string ProcessID { get; } //returns the process ID;
public void StartProcess(); //starts the process;
public int Status { get; } //process status, it can be Unstarted,
InProgress, Complete;
public string ConsumeResults(); //consumes the partial results;
}

The ProcessAgentPool looks like (only declarations. It's nothing but an
*ordinary* Singleton):

public class ProcessAgentPool
{
private static ProcessAgentPool _me = new ProcessAgentPool();
//*unique* instance!!!
private ProcessAgentPool(); //private .ctor();

public static ProcessAgentPool GetInstance(); //this method should
return the *unique*
//ProcessAgentPool instance;
public GetProcessAgent(string processID); //returns the process with
the given ID;
public ProcessAgent CreateProcess(); //returns a new ProcessAgent instance;
}

Pretty simple, isn't it?
Finally, the web service interface looks like:

public class Service : System.Web.Services.WebService
{
internal ProcessAgentPool pool;

public Service()
{
pool = ProcessAgentPool
}

[WebMethod]
public string StartProcess()
{
ProcessAgent p = pool.CreateProcess():
return p.ProcessID;
}
[WebMethod]
public int GetStatus(string processID)
{
ProcessAgent p = pool.GetProcessAgent(processID);
if (p!=null)
{
return p.Status;
}
else {
return -1;
}
}
[WebMethod]
public string GetResults(string processID)
{
ProcessAgent p = pool.GetProcessAgent(processID);
if (p!=null)
{
return p.ConsumeResults();
}
else
{
return "";
}
}
}

As I wrote before, I make all this stuff up in order to have a *unique*
pool instance to keep all the processes going on. I created a small
application to emulate the scenario and it works fine as the HashCode of
the "pool" object is always the same! "Cool!", I said. But I spoke too
early: I tried to implement it in the alfa version of my real
application and something went wrong (Murphy rules!!!). The weird thing
is that *different instances* of my singleton ProcessAgentPool class are
actually instantiated!

So, let's summarize. I have a client application asking for some
long-time-taking operations to be performed by a remote machine, using a
web service (--> stateless). Those processes are "wrapped" in a
ProcessAgent class, all the ProcessAgent class instances are grouped
together in a ProcessAgentPool instance. I need this pool to be *unique*
but it doesn't happens and I can't get *why*, as it works fine in a
sample application I used to study the problem.

Any suggestion? Any alternative way to solve my problem?
Thanks in advance,
Giulio
 
M

Marc Gravell

The weird thing
is that *different instances* of my singleton ProcessAgentPool class are
actually instantiated!

Evidenced by? You didn't say why you think this...
And over what time interval are you seeing tihs?
Are you sure you aren't getting different ProcessAgent instances from a
single ProcessAgentPool?

Basically, by using a singleton, you have exposed yourself to several
threading issues. I wouldn't be at all surprised if a thread race is
causing a failed lookup of a ProcessAgent (in fact, a thread-race here
could crash the whole app, so it is unsafe if you haven't synchronized).

Another possibility is that your app-pool has recycled. This happens. It
also isn't clear on what thread the long-running operation is
happening - if you are using a web-service thread to do the main work, I
might expect starvation (and/or timeouts) to be an issue here as well.

Marc
 
G

Giulio Petrucci

Hi Marc,

thank for having payed attention to my long and boring message and for
your reply! ;-)

Marc Gravell ha scritto:
Evidenced by? You didn't say why you think this...

You're right.
In each web service WebMethod I use the DefaultTraceListener to write
some useful info such:
- ProcessAgentPool hash code,
- ProcessAgentPool count (i.e. how many active process are held by the
pool),
And over what time interval are you seeing tihs?

"Pollings" are 500ms spaced (just for debuggin purpose).
Are you sure you aren't getting different ProcessAgent instances from a
single ProcessAgentPool?

No, different ProcessAgentPool. I set a trace-line (writing on the
DefaulTraceListener) also within the ProcessAgentPool .ctor(), and I can
see that sometimes a new pool instance is created.
Basically, by using a singleton, you have exposed yourself to several
threading issues.
[cut]

I know, but I can ensure that everything is synchronized internally. I
could not post here the whole code of the sample application but you can
trust me. ;-)
Another possibility is that your app-pool has recycled. This happens. It
also isn't clear on what thread the long-running operation is happening
- if you are using a web-service thread to do the main work, I might
expect starvation (and/or timeouts) to be an issue here as well.

I must admit I didn't get this last quote. Please, could you explain a
little?
Thanks a lot,
Giulio
 
M

Marc Gravell

I must admit I didn't get this last quote. Please, could you explain a

I'm assuming that you are hosting your app in IIS; IIS has an option
(enabled by default) to restart an app-pool periodically; as I
understand it, this is intended to solve memory growth issues, and a few
other things. After a while, it will literally destroy your app-pool
(i.e. your app-domain), and the next request will get a nice fresh one -
which will mean that you get a new singleton.

You can disable this, but I'm not sure it wold be my first choice. If I
had a long-running stateful web-service, I would look at using WCF to
host it in a dedicated windows service process (there are sample project
templates for this in VS2008u and VS2005 with the WF extension). That
way, you control the lifetime etc.

Marc
 
G

Giulio Petrucci

Hi Marc,

Marc Gravell ha scritto:
I'm assuming that you are hosting your app in IIS;
[cut]

Ok, I goti it now.
You can disable this, but I'm not sure it wold be my first choice.

Neither do I.
At this point my first choiche would be to create a windows service
process and make my web-service code-behind "talk" to it. My question
is: should I use remoting (or things similar) to make my objects pass
from the windows service context to the web service (IIS) one?
If I
had a long-running stateful web-service, I would look at using WCF
[cut]

Got it.
Anyway, do I need to install FW 3.0, right?

Ciao,
Giulio
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top