1

I'm currently experiencing some issues with workflow services. They work fine if I start 4, 5 in short sequence, but if I increase this value (starting from ~10) then I get the following exception:

This channel can no longer be used to send messages as the output session was auto-closed due to a server-initiated shutdown. Either disable auto-close by setting the DispatchRuntime.AutomaticInputSessionShutdown to false, or consider modifying the shutdown protocol with the remote server.

I think that the problem is in the way I create proxies. I use the following code to provide proxies, attempting to reuse existing ones:

public abstract class ProxyProvider<TService>
    where TService : class
{
    /// <summary>
    /// Static reference to the current time provider.
    /// </summary>
    private static ProxyProvider<TService> current = DefaultProxyProvider.Instance;

    private TService service;

    /// <summary>
    /// Gets or sets the current time provider.
    /// </summary>
    /// <value>
    /// The current time provider.
    /// </value>
    public static ProxyProvider<TService> Current
    {
        get
        {
            return ProxyProvider<TService>.current;
        }

        set
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            ProxyProvider<TService>.current = value;
        }
    }

    /// <summary>
    /// Resets to default.
    /// </summary>
    public static void ResetToDefault()
    {
        ProxyProvider<TService>.current = DefaultProxyProvider.Instance;
    }

    /// <summary>
    /// Loads the proxy.
    /// </summary>
    /// <param name="forceNew">if set to <c>true</c> [force new].</param>
    /// <returns>The instance of the proxy.</returns>
    public virtual TService Provide(bool forceNew = false)
    {
        if (forceNew || !this.IsInstanceValid())
        {
            this.service = this.CreateInstance();
            return this.service;
        }

        return this.service;
    }

    /// <summary>
    /// Internals the load.
    /// </summary>
    /// <returns>The new created service.</returns>
    protected abstract TService CreateInstance();

    private bool IsInstanceValid()
    {
        var instance = this.service as ICommunicationObject;
        if (instance == null)
        {
            return false;
        }

        return instance.State != CommunicationState.Faulted && instance.State != CommunicationState.Closed && instance.State != CommunicationState.Closing;
    }

    /// <summary>
    /// Defines the default <see cref="ProxyProvider&lt;TService&gt;"/> which uses the System DateTime.UtcNow value.
    /// </summary>
    private sealed class DefaultProxyProvider : ProxyProvider<TService>
    {
        /// <summary>
        /// Reference to the instance of the <see cref="ProxyProvider&lt;TService&gt;"/>.
        /// </summary>
        private static ProxyProvider<TService> instance;

        /// <summary>
        /// Gets the instance.
        /// </summary>
        public static ProxyProvider<TService> Instance
        {
            get
            {
                if (DefaultProxyProvider.instance == null)
                {
                    DefaultProxyProvider.instance = new DefaultProxyProvider();
                }

                return DefaultProxyProvider.instance;
            }
        }

        /// <summary>
        /// Loads the specified force new.
        /// </summary>
        /// <returns>A non-disposed instance of the given service.</returns>
        protected override TService CreateInstance()
        {
            var loadedService = Activator.CreateInstance<TService>();
            return loadedService;
        }
    }

With an additional "lazy" provider:

public class CustomConstructorProxyProvider<TService> : ProxyProvider<TService>
    where TService : class
{
    private readonly Func<TService> constructor;

    /// <summary>
    /// Initializes a new instance of the <see cref="CustomConstructorProxyProvider&lt;TService&gt;"/> class.
    /// </summary>
    /// <param name="constructor">The constructor.</param>
    public CustomConstructorProxyProvider(Func<TService> constructor)
    {
        this.constructor = constructor;
    }

    /// <summary>
    /// Internals the load.
    /// </summary>
    /// <returns>The new created service.</returns>
    protected override TService CreateInstance()
    {
        var service = this.constructor();
        return service;
    }
}

Used this way:

var proxy = ProxyProvider<IWorkflowService>.Current.Provide();
proxy.DoSomething();

Initialized like this:

ProxyProvider<IWorkflowService>.Current = new CustomConstructorProxyProvider<IWorkflowService>(() => new WorkflowServiceProxy("endpoint"));

Workflow services are hosted by IIS and I added the following throttling settings:

<serviceThrottling
                maxConcurrentCalls="512"
                maxConcurrentInstances="2147483647"
                maxConcurrentSessions="1024"/>

which should be enough for my needs.

I hope that someone can help me configuring client and server to have achieve the desired scalability (a few hundreds started in sequence and running in parallel, using the WorkflowInstance sql store).

UPDATE: I'm using NetTcpBinding for all services.

UPDATE 2: All services are hosted and consumed by now locally.

Thanks Francesco

fra
  • 3,488
  • 5
  • 38
  • 61
  • Why are you trying to reuse existing ones? Is there a reason to believe creating a new proxy has significant overhead? – Mike Perrenoud Aug 29 '12 at 11:25
  • only because it was always listed as a bad practice. – fra Aug 29 '12 at 12:04
  • 1
    can you give me some understanding as to why you're not just hosting them on the IIS server as SOAP services via the `Receive` activity and just starting them up using that endpoint? I ask this because when I load tested WF, setup just like that, I was able to start up and concurrently run 1000's of instances in seconds. – Mike Perrenoud Aug 29 '12 at 12:07
  • I already used basic http binding, but the system was not responsive enough. In fact the problem at that point was in a different part of code, maybe I can try it again. – fra Aug 29 '12 at 12:10
  • I would recommend that. Further, what are your response time requirements? – Mike Perrenoud Aug 29 '12 at 12:11
  • I don't have specific response time requirements. The state machines implementing the workflow services need to send messages to some devices and collect the feedback. Thanks for your interest. – fra Aug 29 '12 at 12:13
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/15952/discussion-between-mike-and-fra) – Mike Perrenoud Aug 29 '12 at 12:14
  • 1
    I found the solution here: http://stackoverflow.com/questions/2454362/wcf-service-throttling – fra Aug 30 '12 at 04:00

0 Answers0