Exemple #1
0
        /// <summary>
        /// Signals Cadence that the application is capable of executing workflows and/or activities for a specific
        /// domain and task list.
        /// </summary>
        /// <param name="taskList">Specifies the task list implemented by the worker.  This must not be empty.</param>
        /// <param name="options">Optionally specifies additional worker options.</param>
        /// <param name="domain">Optionally overrides the default <see cref="CadenceClient"/> domain.</param>
        /// <returns>A <see cref="Worker"/> identifying the worker instance.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="taskList"/> is <c>null</c> or empty.</exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown when an attempt is made to recreate a worker with the
        /// same properties on a given client.  See the note in the remarks.
        /// </exception>
        /// <remarks>
        /// <note>
        /// <see cref="CadenceClient"/> for more information on task lists.
        /// </note>
        /// <para>
        /// Your workflow application will need to call this method so that Cadence will know
        /// that it can schedule activities to run within the current process.  You'll need
        /// to specify the target Cadence domain and task list.
        /// </para>
        /// <para>
        /// You may also specify an optional <see cref="WorkerOptions"/> parameter as well
        /// as customize the name used to register the activity, which defaults to the
        /// fully qualified name of the activity type.
        /// </para>
        /// <para>
        /// This method returns a <see cref="Worker"/> which implements <see cref="IDisposable"/>.
        /// It's a best practice to call <see cref="Dispose()"/> just before the a worker process
        /// terminates, but this is optional.  Advanced worker implementation that need to change
        /// their configuration over time can also call <see cref="Dispose()"/> to stop workers
        /// for specific domains and task lists.
        /// </para>
        /// <note>
        /// The Cadence GOLANG client does not appear to support starting a worker with a given
        /// set of parameters, stopping that workflow, and then restarting another worker
        /// with the same parameters on the same client.  This method detects this situation
        /// and throws an <see cref="InvalidOperationException"/> when a restart is attempted.
        /// </note>
        /// </remarks>
        public async Task <Worker> StartWorkerAsync(string taskList, WorkerOptions options = null, string domain = null)
        {
            await SyncContext.ClearAsync;

            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(taskList), nameof(taskList), "Workers must be started with a non-empty workflow.");
            EnsureNotDisposed();

            options = options ?? new WorkerOptions();
            domain  = ResolveDomain(domain);

            WorkerMode mode = options.Mode;
            Worker     worker;

            try
            {
                using (await workerRegistrationMutex.AcquireAsync())
                {
                    // Ensure that we haven't already registered a worker for the
                    // specified activity, domain, and task list.  We'll just increment
                    // the reference count for the existing worker and return it
                    // in this case.
                    //
                    // I know that this is a linear search but the number of activity
                    // registrations per service will generally be very small and
                    // registrations will happen infrequently (typically just once
                    // per service, when it starts).

                    // $note(jefflill):
                    //
                    // If the worker exists but its RefCount==0, then we're going to
                    // throw an exception because Cadence doesn't support recreating
                    // a worker with the same parameters on the same client.

                    worker = workers.Values.SingleOrDefault(wf => wf.Mode == mode && wf.Domain == domain && wf.Tasklist == taskList);

                    if (worker != null)
                    {
                        if (worker.RefCount < 0)
                        {
                            throw new InvalidOperationException("A worker with these same parameters has already been started and stopped on this Cadence client.  Cadence does not support recreating workers for a given client instance.");
                        }

                        Interlocked.Increment(ref worker.RefCount);
                        return(worker);
                    }

                    options = options ?? new WorkerOptions();

                    var reply = (NewWorkerReply)(await CallProxyAsync(
                                                     new NewWorkerRequest()
                    {
                        Domain = ResolveDomain(domain),
                        TaskList = taskList,
                        Options = options.ToInternal()
                    }));

                    reply.ThrowOnError();

                    worker = new Worker(this, mode, reply.WorkerId, domain, taskList);
                    workers.Add(reply.WorkerId, worker);
                }
            }
            finally
            {
                switch (mode)
                {
                case WorkerMode.Activity:

                    activityWorkerStarted = true;
                    break;

                case WorkerMode.Workflow:

                    workflowWorkerStarted = true;
                    break;

                case WorkerMode.Both:

                    activityWorkerStarted = true;
                    workflowWorkerStarted = true;
                    break;

                default:

                    throw new NotImplementedException();
                }
            }

            // Fetch the stub for each registered workflow and activity type so that
            // they'll be precompiled so compilation won't impact workflow and activity
            // performance including potentially intruducing enough delay to cause
            // decision tasks or activity heartbeats to fail (in very rare situations).
            //
            // Note that the compiled stubs are cached, so we don't need to worry
            // about compiling stubs for types more than once causing a problem.

            lock (registeredWorkflowTypes)
            {
                foreach (var workflowInterface in registeredWorkflowTypes)
                {
                    // Workflows, we're going to compile both the external and child
                    // versions of the stubs.

                    StubManager.GetWorkflowStub(workflowInterface, isChild: false);
                    StubManager.GetWorkflowStub(workflowInterface, isChild: true);
                }
            }

            lock (registeredActivityTypes)
            {
                foreach (var activityInterface in registeredActivityTypes)
                {
                    StubManager.GetActivityStub(activityInterface);
                }
            }

            return(worker);
        }
Exemple #2
0
        /// <summary>
        /// Signals Cadence that the application is capable of executing activities for a specific
        /// domain and task list.
        /// </summary>
        /// <param name="taskList">Optionally specifies the target task list (defaults to <b>"default"</b>).</param>
        /// <param name="options">Optionally specifies additional worker options.</param>
        /// <param name="domain">Optionally overrides the default <see cref="CadenceClient"/> domain.</param>
        /// <returns>A <see cref="Worker"/> identifying the worker instance.</returns>
        /// <exception cref="InvalidOperationException">
        /// Thrown when an attempt is made to recreate a worker with the
        /// same properties on a given client.  See the note in the remarks.
        /// </exception>
        /// <remarks>
        /// <para>
        /// Your workflow application will need to call this method so that Cadence will know
        /// that it can schedule activities to run within the current process.  You'll need
        /// to specify the target Cadence domain and task list.
        /// </para>
        /// <para>
        /// You may also specify an optional <see cref="WorkerOptions"/> parameter as well
        /// as customize the name used to register the activity, which defaults to the
        /// fully qualified name of the activity type.
        /// </para>
        /// <para>
        /// This method returns a <see cref="Worker"/> which implements <see cref="IDisposable"/>.
        /// It's a best practice to call <see cref="Dispose()"/> just before the a worker process
        /// terminates, but this is optional.  Advanced worker implementation that need to change
        /// their configuration over time can also call <see cref="Dispose()"/> to stop workers
        /// for specific domains and task lists.
        /// </para>
        /// <note>
        /// The Cadence GOLANG client does not appear to support starting a worker with a given
        /// set of parameters, stopping that workflow, and then restarting another worker
        /// with the same parameters on the same client.  This method detects this situation
        /// and throws an <see cref="InvalidOperationException"/> when these restart attempts
        /// are made.
        /// </note>
        /// </remarks>
        private async Task <Worker> StartWorkerAsync(string taskList = "default", WorkerOptions options = null, string domain = null)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(taskList));

            options = options ?? new WorkerOptions();

            WorkerMode mode = options.Mode;
            Worker     worker;

            try
            {
                using (await workerRegistrationMutex.AcquireAsync())
                {
                    // Ensure that we haven't already registered a worker for the
                    // specified activity, domain, and task list.  We'll just increment
                    // the reference count for the existing worker and return it
                    // in this case.
                    //
                    // I know that this is a linear search but the number of activity
                    // registrations per service will generally be very small and
                    // registrations will happen infrequently (typically just once
                    // per service, when it starts).

                    // $note(jeff.lill):
                    //
                    // If the worker exists but its refcount==0, then we're going to
                    // throw an exception because Cadence doesn't support recreating
                    // a worker with the same parameters on the same client.

                    worker = workers.Values.SingleOrDefault(wf => wf.Mode == mode && wf.Domain == domain && wf.Tasklist == taskList);

                    if (worker != null)
                    {
                        if (worker.RefCount == 0)
                        {
                            throw new InvalidOperationException("A worker with these same parameters has already been started and stopped on this Cadence client.  Cadence does not support recreating workers for a given client instance.");
                        }

                        Interlocked.Increment(ref worker.RefCount);
                        return(worker);
                    }

                    options = options ?? new WorkerOptions();

                    var reply = (NewWorkerReply)(await CallProxyAsync(
                                                     new NewWorkerRequest()
                    {
                        Domain = ResolveDomain(domain),
                        TaskList = taskList,
                        Options = options.ToInternal()
                    }));

                    reply.ThrowOnError();

                    worker = new Worker(this, mode, reply.WorkerId, domain, taskList);
                    workers.Add(reply.WorkerId, worker);
                }
            }
            finally
            {
                switch (mode)
                {
                case WorkerMode.Activity:

                    activityWorkerStarted = true;
                    break;

                case WorkerMode.Workflow:

                    workflowWorkerStarted = true;
                    break;

                case WorkerMode.Both:

                    activityWorkerStarted = true;
                    workflowWorkerStarted = true;
                    break;

                default:

                    throw new NotImplementedException();
                }
            }

            return(worker);
        }