Async. BPM execution; still broken 10.2.200.3

This seems more batch-y than asych. You’d be happy if you could put this in the system agent and have it run once an hour. I’m sure there’s some clever coders on this site who know how to do that too. I’ve seen it done.

When I think of async, I think of notifications like logging or sending an email, etc. From a SOLID point of view, a BPM or a Customization would NEVER actually send an email. It would queue the request and then notify a service and be done. The notification service has one job - to send email and deal with all of the issues related to it. The queue would contain when it was submitted, when it was sent, if there was an error, how many retries if there were any, etc. There would be only one place to check the status. There would be only one place to manage the code to email which would give people the chance to use SMTP or even MS Graph.

A good notification service would give the recipient the control of how often and when to receive email. In our Manufacturing sites, we try to replace push systems with pull systems but in IT we push an email for everything every chance we get.

Anything done through the System Agent is async by default but when we use Bar Tender, we build files in our customizations. Instead, we should submit a request to a service that passes all the required information to create and print the label(s) on whatever printer we need. Upgrade Bar Tender? There would be only one service to update and not have to comb through dozens of customizations or BPMs.

This is why I think async in BPMs isn’t really useful. It leads to code that’s harder to maintain by giving more than one job to a function. IMHO - there are better ways to do things now. And we even didn’t talk about the future with WebHooks or Serverless Functions.

1 Like

But I don’t. I want it to work exactly like printing does, where someone clicks go when they want to and it goes and does it’s thing. There are states and syncs into and coming from Epicor, some of which I can’t automate because it’s from another program I don’t have access to like I do in Epicor. I want it on demand so the user can control the timing of the process.

And even if I wanted to hack it to automate it, I shouldn’t have to. (Scheduling BPMs is a well worn topic around here) Async is an out of the box option that simply doesn’t work. It would be like you buying a car when the salesman tells you there is AC, there is a button for it, but it doesn’t work. Can you live without it? Sure. Just throw some ice packs on your lap. Is that what you paid for? No. And frankly it’s actually dangerous because if someone doesn’t know it’s broken, it could have very real business impact when it doesn’t do what it’s supposed to.

3 Likes

You mean a CSV on a file drop? What’s simpler than a CSV? Then the bartender service picks it up? So you want to send all of the information to make a CSV , to then have the service make that CSV, and drop in in the watch folder (a queue essentially) You want a service to trigger another service? How is that better?

We have got round some similar issues by kind of recursive methods, where the user simply triggers the first item in a set of things that need to happen and the remainder are triggered indirectly from that without further intervention. It works, and is pleasingly elegant in its way, but does feel slightly hack-y.

I totally agree that the Async option really SHOULD work since it’s there and apparently available. I had one system that relied on Async that was never able to go into Live use because although it did what it was supposed to 99% of the time, we had the same as @josecgomez reports, and it would fail silently at random intervals and clog the queues. It’s unusable, and it really shouldn’t be because there are valid use cases for it.

1 Like

And you totally could. Technically it’s possible to submit a job to the System Agent to run a C# program that uses the Task class. It’s a perfectly good, well-tested, and working Epicor async solution.

Well, almost. There would be two A/C units in the car. The one that works for the whole car and one that was put in for just the driver and that one doesn’t work reliably. I agree, that one should be removed. :wink:

No CSV?

Two of the major goals of SOLID programming is to reduce dependency and to increase testability. CSV brings with it an extra dependency: the file system. Now you have to worry about permissions, disc space, etc. If I build the CSV in a customization, I have to make sure that each user has access to the share. If I move to a cloud service, I may not have access to that share. If I put the code in five different customizations and something changes (like the share location for a new server), I have to find and update all of those occurrences. If I want to unit test my code, I would have to test it in all customizations.

This is why having a shared code repository in has been a big request for many of us.

It’s better because it isolates all the code in one place which isolates the dependencies and allows for unit testing. Now only one system requires permissions to shares and printers and not every single user.

And I would argue (as probably would Seagull), why create files at all when Bartender can access databases and has it’s own built-in web service integrations?

And Daryl writes:

Queues are difficult. Another one that Epicor has is the Inter-company Processes, which clogs as well with no real tools to manage it. (Integration Workbench shows a subset of clogs but not all.)

Mark W.

I think this is possible now. You can create a multitude of triggers for bartender, and bartender can look into the DB. It would be am interesting project, maybe I should see if I can get something working.

2 Likes

Hi Aaron,

I’ve worked with Asynch BPMs and here is my suggestion. Make it synchronous until you’ve made the whole process very stable. The issue is that when you run into a problem, it’s very difficult to troubleshoot the problem and make the proper fixes to the logic, cause there is almost no way to find out the problem. Even you if generate a log file in the logic, if the process cannot complete 100% and an exception is raised, you won’t get your log file and you are left with records in BPActionQueue and BPActionQueueData that will prevent any other asynch request to be processed.

Once you are good and the logic works perfectly in synchronous mode, revert it to asynch and you should be all right.

But if it EVER FAILS and let’s face it none of is write code that will NEVER FAIL you’ll be back stuck and in square one :frowning:

2 Likes

True. Otherwise, to simulate an asynch process, here is what I’ve also done.

  1. Create a BAQ doing Select company from company
  2. In my custom logic, I’ve automated a launch of a BAQ Export Process using that BAQ
  3. I’ve created a Data Directive on SysTask, make it trap that specific task then run whatever logic you need. Thus, it’s running in the background and the client session is not freezing (likely asynchronous).

It’s important to trap the proper task in the Data Directive, otherwise it will run the logic for any report or task request.

1 Like

Some tips for diagnosing issues with system tasks (which include Async BPM):

First place to look is the Windows Event Log. This is where all errors are logged (excluding business logic validation). Errors are unconditionally written here regardless of whether other forms of logging are enabled. I always recommend creating a view over all the Epicor logs so you can see them all in one place. For any server side issues you can look at this log specifically:
Applications and Services Logs > Epicor App Server

If you are trying to understand more about how it’s all working (or not working), you can get more details from the server log. This needs to be enabled. Here’s a good set of trace flags to enable in your AppServer.config that will give you task related information while not adding too much noise from elsewhere:

<configuration>
  <Trace
    disabled="false"
    verbose="false"
    logLocation="ServerLog.txt">
    <TraceFlags>
      <add uri="trace://ice/fw/extensibility" />
      <add uri="trace://ice/fw/sysagenttask" />
      <add uri="trace://ice/log" />
3 Likes

Thanks @Epic_Santiago. And what are your recommendations for your Cloud Users who do not have access to either the Windows Event Log or the AppServer configs?

The techniques I described above are for server side diagnostics and are available to the server administrator. In the case of cloud, the server administrator is our cloud operations team. So they would be the ones applying those techniques rather than the cloud customer directly. One of the advantages of cloud is that someone else is maintaining the server. So my recommendation would be to open a support ticket describing the issue and let them diagnose what’s causing it.

2 Likes

Hi @Aaron_Moreng from memory UserProcessWrapper is a CSG way to run custom code (defined in a BPM) from a schedule.

By its nature it is already async as it is running as a task and hence that is why async does not make sense here.

Hey Stephen,

I would definitely agree; in an implementation of a UserProcessWrapper BPM as a scheduled task, it would be async by nature. What I am not entirely sure of is why executing a UserProcessWrapper BPM custom code block in “async” mode vs. “sync” mode (by invoking the process manually, not from a schedule) would lead to the async mode version failing to ever execute.
Interestingly enough, using a standard Epicor BO like ABCCode with the same test did work as designed, meaning the async code is written to the BpActionQueue and BpActionData tables and the task agent executes when it can.I only preformed the test 1 time, so I’ll assume it works all the time :wink:

I am not necessarily trying to solve any particular problem but rather bring up an old question regarding asynchronous vs. synchronous BPM custom code execution. There is some great dialog in this thread about different work-around and integration design techniques in order to solve problems.

In my mind, a good use of async BPM would be to invoke a process client side that executes server side with non-UI blocking threading action. The Epicor implementation of this does exactly that, at least to my understanding. When it works as designed, it works just fine and the developer does not have to create any custom queue/polling mechanism.

I think this thread illustrates two valuable points.

The first is that the developer assumes the async. code will be executed. However as we’ve learned/seen, this might not be the case. It was suggested that it might be a server related issue (diagnosed by diligent tracing). Perhaps, but seems strange that so many customers would have this issue.

The second is that when these do fail, they fail silently. Perhaps there is a better way to re-execute failed queued “tasks” and notify when failure does occur instead of just building up in the queue.

Thanks everyone for the great dialog!

2 Likes

Hi folks,

This is my first post on here, but since this is a really common and problematic issue, here is the solution I use for my asynchronous BPMs.

First, create your BPM and flag it as synchronous. Then add a code block to it, and use lambda delegates to define the code to call asynchronously, and call it with .NET’s Tasks.Parallel namespace. This always works, no ifs or buts, and doesn’t rely on a change of mood in Epicor’s code.

// Define a lambda Action<T1, T2, ...> or Func<T1, T2, ...> to be called asynchronously
Action<string> TestMethod =
(path) =>
{
    try
    {
        for (int i = 0; i < 100; i++)
        {
            System.IO.File.AppendAllText(path, i.ToString() + "\r\n"); 
        }
    }
    catch (Exception ex)
    {
        // ...
    }
};


// Call it using the Tasks.Parallel namespace. The BPM data will stay on the heap until the method has finished executing, 
// and then GC will take place. Do not use .BeginInvoke() on the action, as that requires a .EndInvoke(), and if it doesn't
// come BPM data stays on the heap indefinitely.
new System.Threading.Tasks.Task(() => TestMethod(@"C:\temp\Test.txt")).Start();
9 Likes

Hi Hugo,

This is awesome! Is there any way to use the “Db” context to query inside the async Action? When I try to, Epicor has a bad time and crashes. Basically how do we query using the “Db” context async?

The easiest way would be to fetch the data you need before and pass the data to the asynchronous method. If this isn’t possible, you can instantiate your own Erp.ErpContext inside your async method via this constructor overload:

public ErpContext(string connectionString)

You can extract the connection string (Db.ConnectionString) from your existing Db object and pass that to the async method. You may have to add “Enlist=false” to it, to make sure the new connection is not tied to the same transaction context that Epicor declares for its BPM (since Epicor will complete the original transaction context on its own time, and you have no control over it from your secondary thread…).

2 Likes

I have tried several times to use Async BPMs with no luck. My workaround is to use Service connect.

If you have service connect you can create a work flow that will accomplish async and allow more than one bpm to run at a time. Create a work flow that is triggered by dropping a text file with enough information to accomplish what you need the BPM to do. For example it might include the order number and line number to work on. Have the work flow put this information into a UD table. The service connect workflow doesn’t do anything other than putting the information into the UD table. Then build a BPM on the update method on this Table, in the BPM do what you need. By opening up multiple channels/streams service connect can work on more than one occurrence of the file drop. The BPM can create a log file to report any issues and could even email you.

You could accomplish the same with an external file watch program that picks up the file and calls the appropriate BO either via the DLL or Rest.

Thanks Hugo! Yea it seems like staging all needed data before async runs is the most reliable

To be fair, it’s not really more reliable one way or the other… Both methods are equally fine, most of the time.