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)); }
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 })); }
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)); }
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); } } }
public void SimpleSerialization(Dictionary <string, string> payload, string expectedJson) { Assert.Equal(expectedJson, InvocationPayloadSerializer.Serialize(payload)); }