diff --git a/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/activities/object-activities/committing-objects.md b/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/activities/object-activities/committing-objects.md index 203129b92c1..5aa2b08ee92 100644 --- a/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/activities/object-activities/committing-objects.md +++ b/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/activities/object-activities/committing-objects.md @@ -10,7 +10,7 @@ This activity can be used in both microflows and nanoflows. ## Introduction -The **Commit object(s)** activity works on one or more objects. For persistable entities, committing an object stores it in the database. Committing non-persistable entities stores the current attribute values and association values in memory. This allows a rollback to revert to those values. See also [Persistability](/refguide/persistability/). External objects cannot be committed. To store changed values of external objects, use the [Send External Object](/refguide/send-external-object/) activity. +The **Commit object(s)** activity works on one or more objects. For persistable entities, committing an object writes it to the database. Committing non-persistable entities stores the current attribute values and association values in memory. This allows a rollback to revert to those values. See also [Persistability](/refguide/persistability/). External objects cannot be committed. To store changed values of external objects, use the [Send External Object](/refguide/send-external-object/) activity. {{% alert color="info" %}} A Mendix commit does not always behave like a database commit. See [How Commits Work](#how-commits-work), below, for more information. @@ -105,15 +105,39 @@ When inside a [nanoflow](/refguide/nanoflows/), the object is refreshed across t ### Committing Objects -When you commit an object, the current value is saved. This means that you cannot roll back to the previous values of the object using the rollback object activity of a microflow. +When you commit an object, all changes to the current values are saved. This means that you cannot roll back to the previous values of the object using the **Rollback object** activity of a microflow. -However, a Mendix commit is not the same as a database commit. For an object of a persistable entity, the saved value is not committed to the database until the microflow and any microflows from which it is called, complete. This means that errors in a microflow can initiate a rollback. If a microflow activity errors and has **Error handling** set to **Rollback** or **Custom with rollback**, the value of the object is rolled back to the value it had at the start of the microflow. See [Error Handling in Microflows](/refguide/error-handling-in-microflows/) for more information. +However, a Mendix commit is not the same as a database (SQL) COMMIT. When you use a **Commit object(s)** activity, Mendix actually performs an INSERT into the database. For an object of a persistable entity, the saved value is not committed to the database until the microflow and any microflows from which it is called, complete. This means that, although the current end user can see the updated version of the object, other end users will not see the updated object until the microflows end. -Mendix mimics this behavior for non-persistable entities. Committing a non-persistable entity means that you cannot use a rollback object activity to go back to the previous values, although the rollback error handling in a microflow rolls back to the original values. +Another consequence of this is that, in contrast to an explicit **Rollback object** call, errors in a microflow can initiate a rollback. If a microflow activity errors and has **Error handling** set to **Rollback** or **Custom with rollback**, the value of the object is rolled back to the value it had at the start of the microflow. See [Error Handling in Microflows](/refguide/error-handling-in-microflows/) for more information. -{{% alert color="warning" %}} -Deleting an object and then committing it can have different outcomes depending on whether the object has already been committed or not. If the object has already been committed, the delete will remove the object from the database, and the subsequent commit will have no effect. If the object is new (that is, it has not been committed before), the delete will do nothing, but the commit will store the object in the database. Therefore, this sequence of actions (a delete followed by a commit) may lead to unexpected results if the object has not been committed before. -{{% /alert %}} +#### What Gets Committed + +When you work on an object in memory, Mendix records whether the object has been changed. When you perform a **Commit object(s)** activity, the current value is written to the database and Mendix marks the object as unchanged in memory. This has a couple of consequences that you might not expect: + +* If you commit an object which is then rolled back due to an error, committing the object again will not write the latest version to the database. You can understand this as the following sequence (see [Error Handling in Microflows](/refguide/error-handling-in-microflows/) for a more detailed discussion of how rollbacks work during error handling): + + 1. Your microflow starts and creates a savepoint. + 1. You change your object – it is marked as changed. + 1. You perform a **Commit object(s)** activity which sends the changes to the database – the object is marked as unchanged. + 1. An error occurs, the microflow ends and data in the database is rolled back to the savepoint. + 1. You perform a **Commit object(s)** on the object again, but the changes are not written to the database because: + + * The object in memory still has your changes + * The object in memory was marked as unchanged after your previous commit + * The **Commit object(s)** activity does not see the changed marker and so does not recognize that your object in memory has changes which need to be written. + + If you want to keep the changes in the latest version you will have to work around this behavior. + +* Deleting an object and then committing it has different outcomes depending on whether the object has already been committed or not: + + * If the object has already been committed, the delete will remove the object from the database, and the subsequent commit will have no effect. + * If the object is new (that is, it has not been committed before), the delete will do nothing, but the commit will store the object in the database. Therefore, this sequence of actions (a delete followed by a commit) may lead to unexpected results if the object has not been committed before. + + +#### Committing Non-Persistable Entities + +Mendix mimics this behavior for non-persistable entities. Committing a non-persistable entity means that you cannot use a rollback object activity to go back to the previous values, although the rollback error handling in a microflow rolls back to the original values. ### Autocommit and Associated Objects {#autocommit-and-associated-objects} diff --git a/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/events/error-event.md b/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/events/error-event.md index 48e28547e21..3e5fb6153ab 100644 --- a/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/events/error-event.md +++ b/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/events/error-event.md @@ -13,12 +13,12 @@ This event can only be used in **Microflows**. An error event defines where a microflow will stop and throw an error that occurred earlier. If you call a microflow, you may want to know whether any errors occurred within the microflow or not. This event throws the error again, so the caller of the microflow can catch them. When you use this event, all database actions within the current transaction will be rolled back (for more information, see [Error Handling in Microflows](/refguide/error-handling-in-microflows/)). {{% alert color="warning" %}} -You can only use an error event if an error is in scope: Studio Pro does not accept it if you connect the normal execution flow to an error event, because there would not be an error to pass back to the caller. +You can only use an error event if an error is in scope: Studio Pro does not allow you to connect the normal execution flow to an error event, because there would not be an error to pass back to the caller. {{% /alert %}} -In this example, an error occurs while committing an object to the database. It is caught, and the flow continues to the error event where the error is passed back to the caller of the microflow. So you can implement your error handling on multiple levels. +In the example below, an error flow is defined when committing an object to the database. Any error is caught, and the flow continues to the error event where the error is passed back to the caller of the microflow. This allows you to implement your error handling on multiple levels. -{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/events/error-event/error-event.png" class="no-border" >}} +{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/events/error-event/error-event.png" class="no-border" alt="A microflow with a parameter of 'MyEntity'. It has a single action committing 'MyEntity' which has an error flow ending in an error event and the normal flow ending in an end event" >}} {{% alert color="info" %}} When adding an error event, you need to add an [error handler](/refguide/error-handling-in-microflows/#errorhandlers) for an activity before the error event. Link an error event and an activity which has an error handlers set on it with a [sequence flow](/refguide/sequence-flow/) and select **Set as error handler** for the sequence flow. diff --git a/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows.md b/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows.md index 843d1339a25..3f714ccb3f0 100644 --- a/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows.md +++ b/content/en/docs/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows.md @@ -24,6 +24,15 @@ There are four error handling options: * **Custom without rollback** * **Continue** +In summary, the four error handling options do the following: + +| Option | Behavior | +| --- | --- | +| Rollback (default) | All changes are reverted to the state before the microflow started, the microflow aborts, and a system error message is shown. | +| Custom with rollback | All changes are reverted to the state before the microflow started, an error flow is followed, and the microflow's subsequent behavior depends on whether the error handling flow ends with an error or end event. | +| Custom without rollback | Changes made before the error are kept, an error flow is followed, and the microflow's subsequent behavior depends on whether the error handling flow ends with an error or end event. | +| Continue | The microflow proceeds as if no error occurred, keeping all changes, and no error message is logged or displayed to the end-user. | + Except for the **Rollback** option, which is the default, the other three are all custom error handling options. For **Custom with rollback** and **Custom without rollback**, you can create an additional flow from the microflow elements (activities or decisions) from which the error is returned and mark this flow as an error handling flow. You can end this flow with an [error event](/refguide/error-event/) or an [end event](/refguide/end-event/). Hence, we introduce these two custom error handling options based on what event terminates the error handling flow. {{% alert color="info" %}} @@ -40,17 +49,21 @@ In the following subsections, we introduce the functionality of each error handl In this example, the error handling in **Microflow 2** is the default: **Rollback**. -{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/default-roll-back.png" width="600px" class="no-border" >}} +{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/default-roll-back.png" width="600px" class="no-border" alt="Two Mendix microflows. The first microflow, labeled 'Microflow 1,' starts with a green circle labeled 'savepoint 1,' proceeds to 'Create Customer' (NewCustomer, Customer), then calls 'Microflow 2,' and ends with a red circle. The second microflow, labeled 'Microflow 2,' starts with a green circle, proceeds to 'Create Order' (NewOrder, Order), then to 'GenerateError' (Variable, Integer/Long), and ends with a red circle." >}} When **Microflow 1** starts with a button click, a savepoint is created at the very beginning of **Microflow 1**. When an error occurs in **Microflow 2**, the default error handling is to roll back all changes to the state of this savepoint (so all changes are reverted), the microflow is aborted, the error is logged, and a system error message is shown to the end-user. +{{% alert color="warning" %}} +Rollback does not mark any uncommitted objects as changed. See [How Commits Work](/refguide/committing-objects/#how-commits-work) in *Commit Object(s)* to see the implications of this. +{{% /alert %}} + ### Error Handling - Custom with Rollback #### Error Handling Which Ends with an Error Event In this example, the error handling in **Microflow 2** is set to **Custom with rollback** and the error handling flow ends with an error event. -{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/with-roll-back-error-event.png" width="600px" class="no-border" >}} +{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/with-roll-back-error-event.png" width="600px" class="no-border" alt="Two Mendix microflows. The first microflow, labeled 'Microflow 1,' starts with a green circle labeled 'savepoint 1,' proceeds to 'Create Customer' (NewCustomer, Customer), then calls 'Microflow 2,' and ends with an end event. The second microflow, labeled 'Microflow 2,' starts with a green circle, proceeds to 'Create Order' (NewOrder, Order), then to 'GenerateError' (Variable, Integer/Long) which has an error path to 'Log message (error)' which ends with an error event, and the happy route ends with an end event." >}} When **Microflow 1** starts with a button click, a savepoint is created at the very beginning of **Microflow 1**. When an error occurs in **Microflow 2**, everything rolls back to the state of this savepoint because the error handling is set to **Custom with rollback**. Hence, changes made in **Create Order** and in **Create Customer** are both reverted. A custom error is logged using a **Log message** activity. After that, the error event throws an error to terminate **Microflow 2**, and **Microflow 1** will terminate because there is no custom error handing there. @@ -58,7 +71,7 @@ When **Microflow 1** starts with a button click, a savepoint is created at the v In this example, the error handling in **Microflow 2** is set to **Custom with rollback** and the error handling flow ends with an end event. -{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/with-roll-back-end-event.png" width="600px" class="no-border" >}} +{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/with-roll-back-end-event.png" width="600px" class="no-border" alt="Two Mendix microflows. The first microflow, labeled 'Microflow 1,' starts with a green circle labeled 'savepoint 1,' proceeds to 'Create Customer' (NewCustomer, Customer), then calls 'Microflow 2,' and ends with an end event. The second microflow, labeled 'Microflow 2,' starts with a green circle, proceeds to 'Create Order' (NewOrder, Order), then to 'GenerateError' (Variable, Integer/Long) which has an error path to 'Log message (error)' which ends with an end event, and the happy route ends with an end event.">}} When **Microflow 1** starts with a button click, a savepoint is created at the very beginning of **Microflow 1**. When an error occurs in **Microflow 2**, everything rolls back to the state of this savepoint because the error handling is set to **Custom with rollback**. Hence, changes made in **Create Order** and in **Create Customer** are both reverted. A custom error is logged using a **Log message** activity. @@ -70,7 +83,7 @@ Setting **Custom without rollback** will not stop data changes within the activi In this example, the error handling in **Microflow 2** is set to **Custom without rollback** and the error handling flow ends with an error event. -{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/without-roll-back-error-event.png" width="600px" class="no-border" >}} +{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/without-roll-back-error-event.png" width="600px" class="no-border" alt="Two Mendix microflows. The first microflow, labeled 'Microflow 1,' starts with a green circle labeled 'savepoint 1,' proceeds to 'Create Customer' (NewCustomer, Customer), then calls 'Microflow 2,' and ends with an end event. The second microflow, labeled 'Microflow 2,' starts with a green circle, proceeds to 'Create Order' (NewOrder, Order), then to 'savepoint 2' before 'GenerateError' (Variable, Integer/Long) which has an error path to 'Log message (error),' and this error path ends with an error event. The main flow of 'Microflow 2' ends with an end event." >}} When **Microflow 1** starts with a button click, a savepoint is created at the very beginning of **Microflow 1**. Another savepoint is created right before **GenerateError** because the error handling is set to **Custom without rollback**. When an error occurs in **Microflow 2**, changes made in **Create Order** are at this moment still kept because of the savepoint right before **GenerateError**. A custom error is logged using a **Log message** activity. After that, the error event throws an error to terminate **Microflow 2** and rolls back everything to the state of **savepoint 1** which is at the very beginning of **Microflow 1**. Hence, changes made in **Create Customer** and in **Create Order** are both reverted. @@ -82,7 +95,7 @@ Using custom error handling without rollback and ending the error handling flow In this example, the error handling in **Microflow 2** is set to **Custom without rollback** and the error handling flow ends with an end event. -{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/without-roll-back-end-event.png" width="600px" class="no-border" >}} +{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/without-roll-back-end-event.png" width="600px" class="no-border" alt="Two Mendix microflows. The first microflow, labeled 'Microflow 1,' starts with a green circle labeled 'savepoint 1,' proceeds to 'Create Customer' (NewCustomer, Customer), then calls 'Microflow 2,' and ends with an end event. The second microflow, labeled 'Microflow 2,' starts with a green circle, proceeds to 'Create Order' (NewOrder, Order), then to 'savepoint 2' before 'GenerateError' (Variable, Integer/Long) which has an error path to 'Log message (error),' and this error path ends with an end event. The main flow of 'Microflow 2' ends with an end event.">}} When **Microflow 1** starts with a button click, a savepoint is created at the very beginning of **Microflow 1**. Another savepoint is created right before **GenerateError** because the error handling is set to **Custom without rollback**. When an error occurs in **Microflow 2**, changes made in **Create Order** are kept because of the savepoint right before **GenerateError**. A custom error is logged using a **Log message** activity. **Microflow 1** does not receive the error and continues to its end. Changes made in **Create Customer** are kept. @@ -92,7 +105,7 @@ The **Continue** option can only be set on a Call microflow activity or on a loo In this example, the error handling in **Microflow 2** is set to **Continue**. -{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/continue.png" width="600px" class="no-border" >}} +{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/continue.png" width="600px" class="no-border" alt="Two Mendix microflows. The first microflow, labeled 'Microflow 1,' starts with a green circle labeled 'savepoint 1,' proceeds to 'Create Customer' (NewCustomer, Customer), then calls 'Microflow 2,' and ends with an end event. The second microflow, labeled 'Microflow 2,' starts with a green circle, proceeds to 'Create Order' (NewOrder, Order), then to 'savepoint 2' before 'GenerateError' (Variable, Integer/Long) which has a combined error and normal flow which proceeds an end event.">}} When **Microflow 1** starts with a button click, a savepoint is created at the very beginning of **Microflow 1**. Another savepoint is created right before **GenerateError** because the error handling is set to **Continue**. When the error in **Microflow 2** occurs, the microflow continues as if nothing happened. Changes made in **Create Customer** and **Create Order** are both kept. No error message is logged and no message is displayed to the end-user. @@ -106,7 +119,7 @@ You should be very careful with using the **Continue** option since it can make In this example, the error handling on the **GenerateError** activity in **Microflow 2** and on the call of **Microflow 2** are both set to **Custom without rollback**. The error handling flow in **Microflow 2** ends with an error event. -{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/combination-without-roll-back-error-event.png" width="600px" class="no-border" >}} +{{< figure src="/attachments/refguide/modeling/application-logic/microflows-and-nanoflows/microflows/error-handling-in-microflows/combination-without-roll-back-error-event.png" width="600px" class="no-border" alt="Two Mendix microflows. Microflow 1 starts with savepoint 1, creates a customer, then calls Microflow 2. It has a savepoint 2 before the call to Microflow 2, and ends with a red end event. An error path from Microflow 2 also leads to a red end event. Microflow 2 starts, creates an order, has savepoint 3, then generates an error. The main flow of Microflow 2 ends with a red end event. An error path from GenerateError logs a message and ends with an orange error event." >}} There are three savepoints. When **Microflow 1** starts with a button click, a savepoint is created at the very beginning of **Microflow 1**. Another savepoint is created right before the call of **Microflow 2**, and the third savepoint is created right before **GenerateError** - this is because the error handling on the **GenerateError** activity in **Microflow 2** and on the call of **Microflow 2** are both set to **Custom without rollback**. @@ -171,8 +184,29 @@ Consider the following best practices for error handling: * Always use a log activity to print the error message and stack trace. * Add custom error handling for activities that depend on third-party systems, as these activities might fail and usually provide detailed information on the cause of failure. With custom error handling, you should log the object that might have caused the error, the error type and message, or any responses from REST, SOAP, or Java action calls. * Where possible, avoid using the **Continue** option and use **Custom without rollback** instead. +* Do not try to commit objects which were rolled back, Mendix will no longer see them as being changed. See [How Commits Work](/refguide/committing-objects/#how-commits-work) in *Commit Object(s)* for more information about this. * Do not overdo it – you can specify a lot of complicated error handling combinations, but this makes it more difficult (and slower) for the Mendix Runtime to evaluate the microflow, and it also makes it more difficult to predict the exact behavior in case of an exception. +## What Error Handling Gives You + +### Consistent Data + +Everything that happens in your app happens in a transaction. By default either all instructions are completed or nothing is completed. If you don't specify any error handling and the microflow you are trying to run encounters an error, it will appear that nothing has changed. That means that all the objects you created or changed will be reverted, and the end-user will get an error. This ensures that processes and data remain consistent. + +### Isolated Changes + +While updating or creating your objects, you do not want other end-users to see information until your microflow has finished executing. + +To ensure that every end-user or process can only see persisted data, data changed in a microflow is only available within that microflow. None of the changes made inside the microflow will be available outside the microflow, not even to the end-user that initiated the microflow. The information will only be available to all end-users of the application once the microflow has successfully completed all the activities. + +### Protection from Parallel Updates + +When an object is updated, your app will place a lock on that object until the transaction/microflow ends. While the microflow is running, nothing else can read or write that same object and anything that attempts to do so will have to wait. This lock is released automatically as soon as the microflow ends, allowing any waiting processes to continue normally. + +{{% alert color="info" %}} +This doesn't prevent two end-users editing the same object. Two end-users can open the same object and change it. However, neither will see the changes that the other has made. They can both commit the changes and the change committed second will be applied. +{{% /alert %}} + ## Read More * [Error Handling in Nanoflows](/refguide/error-handling-in-nanoflows/)