MSDN Magazine - October 2008 - (Page 129) JUVAL LOWY Foundations Managing State With Durable Services Long-running processes are common in distributed computing. Some business processes are made up of multiple execution sequences which may last many days or even weeks. They may involve clients (or even end users) that connect to the application, perform a small amount of work, transition the workflow to a new state, and then disconnect for an indeterminate amount of time before connecting again and continuing the workflow. The clients may at any point decide to terminate the workflow and start a new one, or the back-end workflow service may end it. With this intermittent flow, do you keep proxies and services in memory waiting for the clients to call? In this column I will discuss several techniques for supporting such long-running services and answer some of these common questions. I will also present the accompanying Windows Communication Foundation (WCF) facilities and a few helper classes I have developed. Figure 1 FileInstanceStore public interface IInstanceStore where ID : IEquatable { void RemoveInstance(ID instanceId); bool ContainsInstance(ID instanceId); T this[ID instanceId] {get;set;} } public class FileInstanceStore : IInstanceStore where ID : IEquatable { public FileInstanceStore(string fileName); } //Rest of the implementation Instance ID and Durable Storage With long-running services, there is obviously little point in keeping proxies and services in memory while waiting around. Such an approach will not withstand the test of time; at the very least, timeout issues will eventually cause the connection to terminate, and there is no easy way to allow machines on either side to reboot or log off. The solution is to avoid keeping the service state in memory, and to handle each call on a new instance with its own temporary in-memory state. For every operation, the service should retrieve its state from some durable storage (such as a file or a database), perform the requested unit of work for that operation, and save the state back to the durable storage at the end of the call. A service that operates in this manner is called a durable service. Because the storage can be shared, durable services can also scale across multiple machines, be it for scalability, redundancy or maintenance purposes. Before the actual work of the long-running process begins, the service must write its state to the durable storage so that subsequent operations will be able to find it. When the workflow ends, the service must remove its state; otherwise, over time the store will become bloated with unnecessary state information. Since a new service instance is created for every operation, the instance must have a way of looking up and loading its state from the durable storage. The client must therefore provide some state identifier for the instance. That identifier is called the instance ID. To support clients that occasionally connect to the service and client applications or even machines that recycle between calls, the client will typically save the instance ID in its own durable storage and provide that ID for every call. When the workflow ends, the client can discard that ID. It is important that your instance ID is serializable and equatable. The ID must be serializable because the service will need to save the ID along with its state into the durable storage. It must be equatable so the service can look up the state from the storage. The durable storage is usually some kind of a dictionary that pairs the instance ID with the instance state. The service that uses it typically uses a single data structure to store all of its state rather than storing individual instance members with unique IDs. The service often then uses a dedicated helper class or a structure to aggregate all its member variables, and stores and retrieves that type from the durable storage in one operation. Access to the durable storage itself must be thread-safe and synchronized because multiple instances may try to access and modify the store concurrently. To help you implement and support simple durable services, I wrote the FileInstanceStore class that you can see in Figure 1. FileInstanceStore is a general-purpose, file-based instance store that takes two type parameters: the ID type parameter, which is constrained to be an equable type, and the T type parameter, which represents the instance state. Additionally both types must be serializable. Send your questions and comments to mmnet30@microsoft.com. Code download available at msdn.microsoft.com/magazine/cc135911. October 2008 129 http://msdn.microsoft.com/magazine/cc135911
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.