This is a practical guide for chaning objects in microflows that also consume webservices from other systems that immediately consume your own published webservices, attempting to change the exact same objects at the same time. A problem emerges when this happens. The moment you change your object, it gets locked and prevents other external methods, including microflows and therefore published webservices from changing the same object for the duration of your microflow’s execution. Even if you commit your object before calling the webservice that calls you back immediately. The result is, your webservice eventually times out and your microflow eventually throws an error.
This is because both the Commit– and Change-with-commit actions don’t really persist your objects immediately, they merely mark the objects in a sub-transaction. The changed objects only get persisted to the database the moment the microflow has finished executing. So while we wait for the flush, it creates a lock on your object we call pessimistic locking. In most cases pessimistic locking is a good thing since it ensures the state consistency of your data, but it prevents other microflows — and published webservices — from changing said object while your microflow is running. While this is the case, sometimes we might want different behaviour, namely synchronised parallel-transactions; meaning transactions running in sequence but separately from each other.
Think inside the box; think outside your app
There are a few solutions to solve this challenge and each method has its pros and cons; most of them being solutions outside of the Mendix-box, not out of the the Mendix box. For this guide, we are using the principle of contentment, meaning the affirmative and creative virtue of maximising what we already have to its fullest.
Since we are already affecting integration points, why not treat ourselves as another integration point? A bit unorthodox for sure, but if you think about it, its just another point in your business transaction.
The key to unlock changing objects
The key as we said is parallel transactions, which we can achieve by consuming published webservices on ourselves that do the change for us. There are other ways, technical ways of achieving parallel transactions, but as we said that probably requires custom coding and a real tech guru. Lets rather use what we have, namely webservices.
Take the first microflow example illustration. Instead of using the Change action in our microflow, we now call a webservice that we have published from our application that does the change. This allows the action to execute in its own thread and therefore its own transaction. Our object is no longer locked when we invoke another system that also wants to change the object that we have changed initially.
This is a simple and elegant out-of-the-box solution. However, take care and use this pattern sparingly and responsibly. It requires cognisance in design however, since we’re now working with synchronised parallel transactions that require separate rollback actions if something goes wrong in our current microflow, instead of the one provided by the business server.
A few good Gotchas
Custom rollback of Transaction if something goes wrong
The first one we’ve already mentioned, namely the rollback of an integration point. This can be treated multiple ways which we might cover in a future post if there is interest. For now it is important to know that your microflow’s conventional rollback mechanism wont roll back the change made by the webservice we invoked on ourself.
Something smells wrong here – Stale object states
When an integration point changes your object’s state while you had it, you will have to re-retrieve it from the database if you drive logic from the object itself in its latest state. Since the parallel transaction has affected its state, you will have a stale object. Simply retrieving from associations wont work, you need to get it fresh from the database.
Need for speed
Since you are invoking an extra webservice endpoint, your microflow might take a bit longer to execute. Always profile your system and determine if your app still performs within what non-functional requirements. At least you have a rapid-prototyping pattern that you can use immediately, and optimise later.
This is a neat way of achieving parallel transactions. You will now be able to change objects without locking them. More importantly however, you did it with the tools at your disposal which means better maintainability. And lets face it, its more fun maximising what your already have.
Thanks to Adam for inventing, or shall I say, for tugging at the problem.
If you want to see the problem in action, download the sample projects, otherwise if you want instant gratification, get out the popcorn: