This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Where is the transaction cache in VAF task queue?

Task queues in Vault Application Framework applications (m-files.com)

I am using a task queue to replicate the document into an external system and I need to keep a reference to that system id.

Usually I put that id into a document property and I use the transaction cache to prevent other event handler to trigger on the document (aka MultiChangePreventor in CK).

Unfortunately the samples using task queue do not have any environment variables as method parameter, unlike event handler.
Is there any way I can have access to the transaction cache to prevent my task queue and handlers to call each other indefinitely ?

Parents
  • You could call a vault extension method from the task processor.

  • It is a bit unfortunate that in Hybrid transaction mode, the Job.Commit do not expose the underlying Environment because it uses the TransactionRunner that have it available in the callback. I just hope this will be adjust in a future release.

    So my option is to go Unsafe and have my code use the TransactionRunner directly to get access to Environment but I lose the automatic handling of job status,

    Or stay Hybrid and call an extension method like you suggest, which is a bit weird because the TransactionRunner does call an extension to have the transaction, which make and extension inside an extension, and the error handling is a bit worst.

  • I can see why you would like to have access to the full event handler environment information here.  What would you like to do differently for event handling though?

  • I just like to have the api updated to support

    job.Commit((vault, environment) =>

  • I understand.  I would like to understand a little more about what impact this has on your error handling though.

  • If I understand correctly in Hybrid, any exception thrown will be grab automatically at higher call level and the job will be requeue. Calling commit will mark the job as completed.

    I was about to create an internal exception class that will have a status and based on that status, I want different job result. Maybe I will just use the AppTaskException.

    But my concern is what happens when I throw that exception inside the extension method.
    Will it be processed correctly by the job method?

  • An exception will be thrown, but it may not be exactly the exception that's thrown inside the commit method; that exception has gone through serialization and back.

    But I think you could probably pass the exception back through, something like this:

    Exception e = null;
    try
    {
      job.Commit((transactionalVault) =>
      {
        e = new AppTaskException(...);
        throw e;
      });
    }
    catch
    {
      // Now do something with "e", which is the actual exception you wanted.
    }
    
    

    I know that we did have some oddities in the original implementation of this whereby the commit call wouldn't run exactly where you expect, but I think the above should work now.

  • I'll have to do more testing on this. Right now I have the extension method in place and that part works perfectly.

    That's what I mean by "the error handling is a bit worst." 

  • The exception received is a COMException and not what was thrown inside the extension method.
    The InnerException is also null.

    This is what the test look like.

    // For reference
    //ObjVerEx doc;
    
    try
    {
      job.Commit((transactionalVault) =>
      {
        // This extension method throw an exception
        transactionalVault.ExtensionMethodOperations.ExecuteVaultExtensionMethod("someextension", doc.ToString());
      });
    }
    catch (Exception e)
    {
      // e is a COMException and the InnerException is null, not the real exception
    }

Reply
  • The exception received is a COMException and not what was thrown inside the extension method.
    The InnerException is also null.

    This is what the test look like.

    // For reference
    //ObjVerEx doc;
    
    try
    {
      job.Commit((transactionalVault) =>
      {
        // This extension method throw an exception
        transactionalVault.ExtensionMethodOperations.ExecuteVaultExtensionMethod("someextension", doc.ToString());
      });
    }
    catch (Exception e)
    {
      // e is a COMException and the InnerException is null, not the real exception
    }

Children
  • You're correct, but did you try the approach I showed?  You can declare a variable for the exception before the commit call, then reference that outside.  Note in my code sample I wasn't using "catch(Exception e)" - the variable was declared higher up.

    For clarity:

    // Declare "e" here.
    Exception e = null;
    
    try
    {
        job.Commit((transactionalVault) =>
        {
            // Create the exception to throw, and store in e...
            e = new AppTaskException(TaskProcessingJobResult.Retry, "hello world");
            
            // ...then throw e.
            throw e;
        });
    }
    catch(Exception restoredException)
    {
        // Notice that this catches "restoredException", but "e" is declared on line 2.
        // "restoredException" will contain the restored exception, which may not be as useful.
        // "e", though, will contain the exact exception that you threw inside your code.
        // This means you can do whatever you want with the exception including logging
        // and re-throwing to control task re-queuing and the like.
    }

  • Yeah I did and got my exception.

    Now I am trying to implement a notification when a task fails (ie at the fatal stage, I do not want to be notify if it get requeued or retried). I was thinking about using NLOG email setting for that.

    My general idea is to use the TaskManager event because I have several tasks and want a central place to handle that.

    protected override void InitializeTaskManager()
    {
        base.InitializeTaskManager();
    
        if (TaskManager != null)
        {
            //LogManager.EnableLoggingToAttachedDebugger(true);
    
            TaskManager.TaskEvent += (s, e) =>
            {
                // When my task fails, log a fatal error
                if (e.EventType == TaskManagerEventType.TaskJobFinished && e.JobResult == TaskProcessingJobResult.Fatal && e.Queues.Safe().Contains(MyTaskQueueId))
                    Logger.Fatal(e.Exception, $"TEST");
            };
        }
    }

    Unfortunately it seems that the Job.Exception is not carried on the task event (e.Exception is always null).
    I can still find most of my needed info in the JobStatus ErrorMessage and JobStatus.StackTrace.

    Maybe you can add some kind of improvement here

    // THIS IS FROM TaskQueueProcessor CLASS
    
    private void JobDone(Task<TaskProcessingJobResult> task, TaskProcessingJob job)
    {
    	EnterWriteLock();
    	try
    	{
    		activeJobs.Remove(job.TaskInfo.TaskID);
    	}
    	finally
    	{
    		ExitWriteLock();
    	}
    	taskCompletedSinceLastPoll = true;
    	
    	// MAYBE ADD THE job.Exception TO THE TaskManagerEventArgs CONSTRUCTOR
    	Manager.TriggerTaskEvent(new TaskManagerEventArgs(TaskManagerEventType.TaskJobFinished, new string[1] { Id }, null, new ApplicationTaskInfo[1] { job.TaskInfo }, null, job.GetStatus(), task.Result));
    }