次の方法で共有


REST Starter Kit, ServiceBehavior and Thread safety

If you have seen the REST Starter Kit (Preview 1) perhaps you noticed a TODO comment about the ServiceBehavior attribute.  We decided to make this comment a little more explicit in Preview 2.

 

 /// TODO: Modify the service behavior settings (instancing, concurrency etc) based on the service's requirements. 
/// Use ConcurrencyMode.Multiple if your service implementation is thread-safe.

/// TODO: NOTE: Please set IncludeExceptionDetailInFaults to false in production environments

[ServiceBehavior(IncludeExceptionDetailInFaults = true, 
    InstanceContextMode = InstanceContextMode.Single, 
    ConcurrencyMode = ConcurrencyMode.Single)]

 

What does this ServiceBehavior attribute do?  It controls the runtime behavior of the service.   Let’s examine what each of the settings mean.

IncludeExceptionDetailsInFaults

This value controls how WCF will respond to unhandled exceptions.  When in development it is helpful to have this value set to true so that the client application can see what errors the server is throwing.  Typically for production systems you turn this off.

InstanceContextMode

This value controls how WCF will create new objects to service clients.  The important thing to notice here is that the default setting for services in the WCF Starter Kit template is InstanceContextMode.Single.  This means you will have only one instance of your service class created by WCF to service all clients.

ConcurrencyMode

This value controls how WCF will dispatch calls to your service.  The default value for the starter kit is ConcurrencyMode.Single.  This means that your service will process only 1 request at a time on the 1 service object created by WCF.

What do the default values give me?

With these values you have a singleton.  One instance of your service class that can process 1 request at a time.  This means that you can write code that is not thread safe and you will be fine, but the scalability of your service will be limited.

What values should I use?

If you can write your class so that it is thread safe, set the concurrency mode to ConcurrencyMode.Multiple.  This means that though you have only once instance of your service, multiple threads will call into it allowing your service to be more scalable.

How should I design my service class to be thread safe?

Your service class should be a pretty thin layer, a facade that simply forwards calls to a business logic or data layer.  You want to avoid the use of shared state in your service class as you process requests.

What state is shared?

This depends on the InstanceContextMode value. 

InstanceContextMode.Single

When it is set to Single then you have only one service instance created.  Any fields or properties declared at the class level will be shared among all instances.  This is ok if those objects are thread safe, if they are not you will have to do something to make the code thread safe.

Static members are shared as well and they will have to be synchronized if they are not thread safe.

InstanceContextMode.PerSession / PerCall

Because the RESTful services do not support sessions either of these settings will result in PerCall behavior.  This means that every request to your service will have a new object created to handle the call.  In this case member fields or properties will not be shared but static members will be and must be synchronized.

This makes my head hurt, just tell me what should I do

Design your service so that it and the business logic it calls is stateless.  Treating each call as a separate task and not sharing state between calls. 

For example, a typical service will do something like this

 protected override WineData OnGetItem(string id)
{
    Int32 wineID;
    if (!Int32.TryParse(id, out wineID))
    {
        throw new WebProtocolException(HttpStatusCode.BadRequest);
    }
    return CohoDB.WineGateway.GetWine(wineID);
}

Is this code thread safe?  What you see here is, but the CohoDB.WineGateway.GetWine method may or may not be so you have to look at what it does.

 public WineData GetWine(int wineID)
{
    using (CohoDBEntities context = new CohoDBEntities())
    {
        return (WineData)context.Wines.
            Include("Accolades").
            Include("WineTypes").
            FirstOrDefault((w) => w.WineID == wineID);
    }
}

This simple class uses the ADO.NET Entity Framework to create a context and get the WineData.  Each request to GetWine will create a new context so it is thread safe.

Because my code is thread safe I can change the ServiceBehavior for my service.

 [ServiceBehavior(
// Still in development - I want exception details    
IncludeExceptionDetailInFaults = true,

// Only create one instance of the service class - it has no state to protect

InstanceContextMode = InstanceContextMode.Single,

// Process requests from multiple threads on the shared instance 

// the data layer is thread safe
ConcurrencyMode = ConcurrencyMode.Multiple        
)]

If this is so great, why does the starter kit default to being a Singleton?

Because the sample code included in the template uses shared state.  It services requests from a Dictionary and there is no locking to synchronize access to the shared state.

Be Careful about Momentum

I found that many developers ignore things in the code that they don’t understand.  I’ve been guilty of this myself.  You assume that the default setting must be the best possible setting and because you are busy and don’t have the time to think it through you deploy your app into production with the setting included in the code from the template.

With RESTful services based on this template, the end result of this will be a safe but not very scalable implementation.  My advice to you is that you carefully consider the thread safety of your code all the way down the call stack.  Avoid sharing state across calls and set the concurrency mode to Multiple if you can be sure that you are thread safe for a more scalable service.

Comments

  • Anonymous
    March 03, 2009
    Thank you for submitting this cool story - Trackback from DotNetShoutout

  • Anonymous
    March 03, 2009
    When does Preview 2 come out?  (launched with the Beta with everything else in a couple weeks?)

  • Anonymous
    March 03, 2009
    Yes - same time

  • Anonymous
    March 04, 2009
    Good to know! I actually hadn't updated to Preview 1 yet -- and just assumed it would run multi-threaded just like any service. Thanks for giving this some visibility. By the way, will Preview 2 have a go-live license?