Persistable Workflow with MVC - Run throwing exception on completed - asp.net-mvc-3

I'm running a persistable Workflow in an MVC 3 Application, which is working out well, but when the workflow is completed, a WorkflowApplicationCompletedException is thrown. The Workflow is completed sucessfully, the last actions done and the instance deleted from the database.
I've had no luck searching for an answer so far, so any ideas what is causing the exception would be appreciated. My current workaround is catching the exception and doing my stuff there for the OnCompleted-Event.
I'm simply creating a WorkflowApplication, loading it and resuming the bookmark.
Any hints or suggestions appreciated.
Thanks
application.Load(new Guid(basket.StandardFields.Settings));
application.ResumeBookmark(application.GetBookmarks().First().BookmarkName, WorkflowInputs);
application.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e)
{
if (e.Bookmarks != null && e.Bookmarks.Count > 0)
{
_viewName = e.Bookmarks[0].BookmarkName;
}
syncContext.OperationCompleted();
return PersistableIdleAction.Unload;
};
application.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
CompleteWorkflow(syncContext);
};
application.SynchronizationContext.OperationStarted();
try
{
application.Run();
}
catch(WorkflowApplicationCompletedException)
{
CompleteWorkflow(syncContext);
}
Edit
It seems that the application.ResumeBookmark(bookmark, WorkflowInputs) starts the Workflow and Completes the activities, then when I call run, it complains the it's already completed. But if I don't call run when resume workflow is called, the browser never gets any information and I think it stays waiting endlessly cause not even a refresh can knock it out of the waiting state.

It seems that with ResumeBookmark there is no need to call Run afterwards. I think I was doing it at the wrong place before and so the workflow got messed up, but it seems to be working fine now.
if(hasWorkflow)
application.ResumeBookmark(application.GetBookmarks().First().BookmarkName, WorkflowInputs);
else
application.Run();

MSDN:
Represents the exception that is
thrown when an operation on a workflow
instance is not valid because the
instance has completed.
The code you show appears valid. However, somewhere you are attempting to resume a workflow that has entered the completed state. You should be checking the Completed property of any Workflow you are attempting to resume. Thrown an InvalidOperationException and you'll see where this is happening.
If this doesn't identify where the problem is, your workflow may not be bookmarking properly. That code is in the activity that is creating the bookmark, so I can't tell if it is being done correctly...

Related

Finish workflow when activity goes wrong

I have a workflow that executes a couple of activities. When the activity finish, it returns an Outcome either Done or Cancel, from outside and before running the next activity, I need to check if the previous activity was ok or not, in case not, I need to cancel the workflow. I have this
public class CreateEmployee : IWorkflow
{
public void Build(IWorkflowBuilder builder)
{
builder
.WithDisplayName(this.GetType().Name)
.Then<GetDataById>(x => x.WithDisplayName(x.ActivityType.Name))
.When(OutcomeNames.Cancel).Finish()
.Then<InsertEmployee>(x => x.WithDisplayName(x.ActivityType.Name))
.When(OutcomeNames.Cancel).Finish()
.Then<InsertMapping>(x => x.WithDisplayName(x.ActivityType.Name))
.When(OutcomeNames.Cancel).Finish();
}
}
For example, after executing activity GetDataById, if the return is "Cancel", I call Finish(), is this going to stop just the activity and continue the workflow or the workflow will stop completely? I'm not able to test it because I'm using DI and I need to prepare the whole unit test, because I didn't find anything directly related to cancel the whole workflow
I'm not sure if I have fully understood your question, but in the documentation about finish activity it's stated that:
when this activity is used within a workflow, the workflow instance
will enter the Finished state. When used in a child composite
activity, that activity will stop execution and yield back control to
its container. However, it will not stop workflow execution itself.

Cancel running job scheduled with Hangfire.io

I schedule job using hangfire.io library and I can observe it being processed in built in dashboard. However, my system has requirement that the job can be cancelled from the dashboard.
There is an option to delete running job, but this only changes the state of the job in database and does not stop running job.
I see in documentation there is option to pass IJobCancellationToken however as I understand it it is used to correctly stop the job when whole server is shutting down.
Is there a way to achieve programmatic cancellation of already running task?
Should I write my own component that would periodically pull database and check whether current server instance is running job that has been cancelled? For instance maintain dictionary jobId -> CancellationTokenSource and then signal cancellation using appropriate tokensource.
Documentation is incomplete a bit. IJobCancellationToken.ThrowIfCancellationRequested method throws an exception after any of the following conditions met:
Hangfire Server shutdown initiated. This event is triggered when someone calls Stop or Dispose methods of BackgroundJobServer.
Background job is not in Processed state.
Background job is being performed by another worker.
The latter two cases are performed by querying job storage for the current background job state. So, cancellation token will throw if you delete or re-queue it from the dashboard also.
This works if you delete the job from the dashboard
static public void DoWork(IJobCancellationToken cancellationToken)
{
Debug.WriteLine("Starting Work...");
for (int i = 0; i < 10; i++)
{
Debug.WriteLine("Ping");
try
{
cancellationToken.ThrowIfCancellationRequested();
}
catch (Exception ex)
{
Debug.WriteLine("ThrowIfCancellationRequested");
break;
}
//Debug.WriteProgressBar(i);
Thread.Sleep(5000);
}
Debug.WriteLine("Finished.");
}

NHibernate ArgumentOutOfRangeException

I recently ran into an instance where I wanted to hit the database from a Task I have running periodically within a web application. I refactored the code to use the ThreadStaticSessionContext so that I could get a session without an HttpContext. This works fine for reads, but when I try to flush an update from the Task, I get the "Index was out of range. Must be non-negative and less than the size of the collection." error. Normally what I see for this error has to do with using a column name twice in the mapping, but that doesn't seem to be the issue here, as I'm able to update that table if the session is associated with a request (and I looked and I'm not seeing any duplicates). It's only when the Task tries to flush that I get the exception.
Does anyone know why it would work fine from a request, but not from a call from a Task?
Could it be because the Task is asynchronous?
Call Stack:
at System.ThrowHelper.ThrowArgumentOutOfRangeException()
at System.Collections.Generic.List`1.System.Collections.IList.get_Item(Int32 index)
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
at NHibernate.Engine.ActionQueue.ExecuteActions()
at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
at NHibernate.Impl.SessionImpl.Flush()
Session Generation:
internal static ISession CurrentSession {
get {
if(HasSession) return Initializer.SessionFactory.GetCurrentSession();
ISession session = Initializer.SessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
return session;
}
}
private static bool HasSession {
get { return CurrentSessionContext.HasBind(Initializer.SessionFactory); }
}
Task that I want to access the database from:
_maid = Task.Factory.StartNew(async () => {
while(true) {
if(CleaningSession != null) CleaningSession(Instance, new CleaningSessionEventArgs { Session = UnitOfWorkProvider.CurrentSession });
UnitOfWorkProvider.TransactionManager.Commit();
await Task.Delay(AppSettings.TempPollingInterval, _paycheck.Token);
}
//I know this function never returns, I'm using the cancellation token for that
// ReSharper disable once FunctionNeverReturns
}, _paycheck.Token);
_maid.GetAwaiter().OnCompleted(() => _maid.Dispose());
Edit: Quick clarification about some of the types above. CleaningSession is an event that is fired to run the various things that need to be done, and _paycheck is the CancellationTokenSource for the Task.
Edit 2: Oh yeah, and this is using NHibernate version 4.0.0.4000
Edit 3: I have since attempted this using a Timer, with the same results.
Edit 4: From what I can see of the source, it's doing a foreach loop on an IList. Questions pertaining to an IndexOutOfRangeException in a foreach loop tend to suggest a concurrency issue. I still don't see how that would be an issue, unless I misunderstand the purpose of ThreadStaticSessionContext.
Edit 5: I thought it might be because of requests bouncing around between threads, so I tried creating a new SessionContext that combines the logic of the WebSessionContext and ThreadStaticSessionContext. Still getting the issue, though...
Edit 6: It seems this has something to do with a listener I have set up to update some audit fields on entities just before they're saved. If I don't run it, the commit occurs properly. Would it be better to do this through an event than OnPreInsert, or use an interceptor instead?
After muddling through, I found out exactly where the problem was. Basically, there was a query that was run to load the current user record called from inside of the PreUpdate event in my listener.
I came across two solutions to this. I could cache the user in memory, avoiding the query, but having possibly stale data (not that anything other than the id matters here). Alternatively, I could open a temporary stateless session and use that to look up the user in question.

Delayed Job creating Airbrakes every time it raises an error

def perform
refund_log = {
success: refund_retry.success?,
amount: refund_amount,
action: "refund"
}
if refund_retry.success?
refund_log[:reference] = refund_retry.transaction.id
refund_log[:message] = refund_retry.transaction.status
else
refund_log[:message] = refund_retry.message
refund_log[:params] = {}
refund_retry.errors.each do |error|
refund_log[:params][error.code] = error.message
end
order_transaction.message = refund_log[:params].values.join('|')
raise "delayed RefundJob has failed"
end
end
When I raise "delayed RefundJob has failed" in the else statement, it creates an Airbrake. I want to run the job again if it ends up in the else section.
Is there any way to re-queue the job without raising an exception? And prevent creating an airbrake?
I am using delayed_job version 1.
The cleanest way would be to re-queue, i.e. create a new job and enqueue it, and then exit the method normally.
To elaborate on #Roman's response, you can create a new job, with a retry parameter in it, and enqueue it.
If you maintain the retry parameter (increment it each time you re-enqueue a job), you can track how many retries you made, and thus avoid an endless retry loop.
DelayedJob expects a job to raise an error to requeued, by definition.
From there you can either :
Ignore your execpetion on airbrake side, see https://github.com/airbrake/airbrake#filtering so it still gets queued again without filling your logs
Dive into DelayedJob code where you can see on https://github.com/tobi/delayed_job/blob/master/lib/delayed/job.rb#L65 that a method named reschedule is available and used by run_with_lock ( https://github.com/tobi/delayed_job/blob/master/lib/delayed/job.rb#L99 ). From there you can call reschedule it manually, instead of raising your exception.
About the later solution, I advise adding some mechanism that still fill an airbrake report on the third or later try, you can still detect that something is wrong without the hassle of having your logs filled by the attempts.

android: AsyncTask onPostExecute keep working even if start new activity on doInBackground

i am building an application for clients to get questions from server and answer it, if the server doesn't have questions i want to go to new screen and print message that try again in few minutes, getting questions is in AsyncTask , if the server doesn't have questions , it will sends in the header of the responds, a header isFindAQuestion with the value false, here is the code on client to ensure if false , i print on LogCat and i see the message = false, but my problems that even if i start new activity with the intent, this activity keep working and show me exception and it is null pointer exception because on the onPostExceute will take a parmeter null and try to process it, i put finish() in the end of false statement but doesn't finish the activity
if (response.getFirstHeader("isFindAQuestion").getValue()
.toString().equals("false")) {
Log.d("message", "false");
Bundle basket = new Bundle();
basket.putString("Message", "sorry no enought questions");
Intent goToAnswerQuestion = new Intent(AnswerQuestion.this,
FinishTime.class);
goToAnswerQuestion.putExtras(basket);
startActivity(goToAnswerQuestion);
finish();
}
Editis it because AsyncTask is working on thread so if the activity is finished, that thread will keep working? and if so how can i stop that thread?
doInBackground is not executed in the UI thread, but in a separeted thread:
invoked on the background thread immediately after onPreExecute()
finishes executing. This step is used to perform background
computation that can take a long time.
If you want to stop your background operation and perform some activities on the UI thread the better thing is to call cancel() and then perform all the stuff you want in the onCancelled callback wich is executed on the UI thread.
From the AsyncTask documentation:
A task can be cancelled at any time by invoking cancel(boolean).
Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns.
To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
protected void onCancelled (Result result)
Runs on the UI thread after cancel(boolean) is invoked and doInBackground(Object[]) has finished.
The default implementation simply invokes onCancelled() and ignores the result. If you write your own implementation, do not call super.onCancelled(result).

Resources