Пример #1
0
        public virtual async Task <InvocationState> Enqueue(string job, string source, Dictionary <string, string> payload, TimeSpan invisibleFor, string jobInstanceName, bool unlessAlreadyRunning)
        {
            var invisibleUntil = _clock.UtcNow + invisibleFor;

            string payloadString = null;

            if (payload != null)
            {
                payloadString = InvocationPayloadSerializer.Serialize(payload);
            }

            var row = await ConnectAndExec(
                "work.EnqueueInvocation",
                new
            {
                Job                  = job,
                Source               = source,
                Payload              = payloadString,
                NextVisibleAt        = invisibleUntil.UtcDateTime,
                InstanceName         = InstanceName,
                JobInstanceName      = jobInstanceName ?? job, // Can consider changing the stored procedures to not accept NULL
                UnlessAlreadyRunning = unlessAlreadyRunning
            });

            if (row == null)
            {
                return(null);
            }
            return(new InvocationState(row));
        }
Пример #2
0
 internal static InvocationState CreateInvocation(Guid id, string job, string source, Dictionary <string, string> payload, bool isContinuation)
 {
     return(new InvocationState(
                new InvocationState.InvocationRow()
     {
         Version = 0,
         Id = id,
         Job = job,
         Source = source,
         Payload = payload == null ? null : InvocationPayloadSerializer.Serialize(payload),
         Status = (int)InvocationStatus.Queued,
         Result = (int)ExecutionResult.Incomplete,
         IsContinuation = isContinuation
     }));
 }
Пример #3
0
        public virtual async Task <bool> Suspend(InvocationState invocation, Dictionary <string, string> newPayload, TimeSpan suspendFor, string logUrl)
        {
            var suspendUntil      = _clock.UtcNow + suspendFor;
            var serializedPayload = InvocationPayloadSerializer.Serialize(newPayload);
            var newVersion        = await ConnectAndExec(
                "work.SuspendInvocation",
                new
            {
                Id           = invocation.Id,
                Version      = invocation.CurrentVersion,
                Payload      = serializedPayload,
                SuspendUntil = suspendUntil.UtcDateTime,
                LogUrl       = logUrl,
                InstanceName = InstanceName
            });

            return(ProcessResult(invocation, newVersion));
        }
Пример #4
0
        internal void Update(InvocationRow newVersion)
        {
            // Cast here so that failures occur during the update.
            Status = (InvocationStatus)newVersion.Status;
            Result = (ExecutionResult)newVersion.Result;

            // Set up dates
            LastDequeuedAt  = LoadUtcDateTime(newVersion.LastDequeuedAt);
            LastSuspendedAt = LoadUtcDateTime(newVersion.LastSuspendedAt);
            CompletedAt     = LoadUtcDateTime(newVersion.CompletedAt);
            QueuedAt        = new DateTimeOffset(newVersion.QueuedAt, TimeSpan.Zero);
            NextVisibleAt   = new DateTimeOffset(newVersion.NextVisibleAt, TimeSpan.Zero);
            UpdatedAt       = new DateTimeOffset(newVersion.UpdatedAt, TimeSpan.Zero);

            if (String.IsNullOrEmpty(newVersion.Payload))
            {
                Payload = new Dictionary <string, string>();
            }
            else if (CurrentRow == null || !String.Equals(CurrentRow.Payload, newVersion.Payload, StringComparison.Ordinal))
            {
                Payload = InvocationPayloadSerializer.Deserialize(newVersion.Payload);
            }
            CurrentRow = newVersion;
        }
Пример #5
0
        protected internal virtual async Task Dispatch(InvocationState invocation, InvocationLogCapture capture, CancellationToken cancelToken, bool includeContinuations)
        {
            InvocationContext.SetCurrentInvocationId(invocation.Id);

            if (invocation.IsContinuation)
            {
                InvocationEventSource.Log.Resumed();
            }
            else
            {
                InvocationEventSource.Log.Started();
            }

            // Record that we are executing the job
            if (!await Queue.UpdateStatus(invocation, InvocationStatus.Executing, ExecutionResult.Incomplete))
            {
                InvocationEventSource.Log.Aborted(invocation);
                return;
            }

            // Create the request.Invocation context and start capturing the logs
            await capture.Start();

            var context = new InvocationContext(invocation, Queue, cancelToken, capture);

            InvocationResult result = null;

            try
            {
                result = await Dispatcher.Dispatch(context);

                // TODO: If response.Continuation != null, enqueue continuation
                switch (result.Result)
                {
                case ExecutionResult.Completed:
                    InvocationEventSource.Log.Succeeded(result);
                    break;

                case ExecutionResult.Faulted:
                    InvocationEventSource.Log.Faulted(result);
                    break;

                case ExecutionResult.Incomplete:
                    InvocationEventSource.Log.Suspended(result);
                    break;

                default:
                    InvocationEventSource.Log.UnknownStatus(result);
                    break;
                }

                if (invocation.NextVisibleAt < Clock.UtcNow)
                {
                    InvocationEventSource.Log.InvocationTookTooLong(invocation);
                }
            }
            catch (Exception ex)
            {
                InvocationEventSource.Log.DispatchError(ex);
                result = new InvocationResult(ExecutionResult.Crashed, ex);
            }

            // Stop capturing and set the log url
            string logUrl = null;

            if (capture != null)
            {
                var logUri = await capture.End();

                if (logUri != null)
                {
                    logUrl = logUri.AbsoluteUri;
                }
            }

            if (result.Result != ExecutionResult.Incomplete)
            {
                // If we're not suspended, the invocation has completed
                InvocationEventSource.Log.Ended();
                await Queue.Complete(invocation, result.Result, result.Exception == null?null : result.Exception.ToString(), logUrl);

                // If we've completed and there's a repeat, queue the repeat
                if (result.RescheduleIn != null)
                {
                    // Rescheule it to run again
                    var repeat = await EnqueueRepeat(invocation, result);

                    InvocationEventSource.Log.ScheduledRepeat(invocation, repeat, result.RescheduleIn.Value);
                }
            }
            else
            {
                if (includeContinuations)
                {
                    invocation.Update(new InvocationState.InvocationRow()
                    {
                        Id              = Guid.NewGuid(),
                        Version         = invocation.CurrentVersion + 1,
                        Job             = invocation.Job,
                        Source          = invocation.Id.ToString("N"),
                        Status          = (int)InvocationStatus.Suspended,
                        Result          = (int)ExecutionResult.Incomplete,
                        LastDequeuedAt  = invocation.LastDequeuedAt == null ? (DateTime?)null : invocation.LastDequeuedAt.Value.UtcDateTime,
                        LastSuspendedAt = DateTime.UtcNow,
                        CompletedAt     = null,
                        QueuedAt        = invocation.QueuedAt.UtcDateTime,
                        NextVisibleAt   = invocation.NextVisibleAt.UtcDateTime + DefaultInvisibilityPeriod,
                        UpdatedAt       = invocation.UpdatedAt.UtcDateTime,
                        Payload         = InvocationPayloadSerializer.Serialize(result.Continuation.Parameters),
                        IsContinuation  = true
                    });

                    // Run the continuation inline after waiting
                    await Task.Delay(result.Continuation.WaitPeriod);

                    await Dispatch(invocation, capture, cancelToken, includeContinuations);
                }
                else
                {
                    // Suspend the job until the continuation is ready to run
                    await Queue.Suspend(invocation, result.Continuation.Parameters, result.Continuation.WaitPeriod, logUrl);
                }
            }
        }
Пример #6
0
        public void SimpleDeserialization(Dictionary <string, string> expectedPayload, string json)
        {
            var deserialized = InvocationPayloadSerializer.Deserialize(json);

            Assert.True(expectedPayload.SequenceEqual(deserialized));
        }
Пример #7
0
 public void SimpleSerialization(Dictionary <string, string> payload, string expectedJson)
 {
     Assert.Equal(expectedJson, InvocationPayloadSerializer.Serialize(payload));
 }