So, the service engine has this way cool feature, the ability to call a service when the current transaction is rolled back, or committed. However, in reality, it actually is completely and uterly broken.
Both of these methods allow for persisting the job to the JobSandbox. That in itself is not a problem. What is a problem, is that GenericAbstractDispatcher will *always* make any such registered service an async service. There is no way to run a sync service. Remember this point, I'll come back to it in a bit. Once the ServiceXaWrapper is registered with the transaction, and the actual commit/rollback phase happens, it'll end up calling an internal runService helper method. However, this helper method is run inside a brand new, temporary thread. This effectively makes *all* registered services async. That's just broken. One thing I'm not familiar with, is if a thread that spawns a new one automatically inherits the parent thread's transaction. However, no matter what that scenario actually is, when runService is running in its own separate thread, it suspends the parent transaction. This means it's in its very own separate transaction. So that the service actually being run will *not* see anything that was in the original transaction upon which it was registered. Now comes the time to actually run the real service. Since the service is always an async service, it'll be serialized to JobSandbox, and run a very short time later. Not immediately. Back in the original, parent thread, service, and transaction, the transaction is committed or rolled back. The aforementioned async service(s) are now run. If the transaction was rolled back, and they try to manipulate data, the original data will no longer exist. So, the service will fail, and the JobPoller will schedule a retry of the service. But, the service never has a chance of succeeding, so this will repeating ad infinitum. I was able to track all this down, because we had placeOrder/processPayment run inside a transaction, that was then aborted if the payment was declined. We then started getting spammed in the log with failed sandbox entries. Does anyone else agree with my reading of the code? Just looking at the source I was able to discover these very poorly implemented designs. Does anyone else agree with my reading here?