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..13deb5104fd 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,10 +10,10 @@ 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. +A Mendix commit does not always behave like a database COMMIT. See [How Commits Work](#how-commits-work), below, for more information. {{% /alert %}} ## Properties @@ -103,21 +103,53 @@ When inside a [nanoflow](/refguide/nanoflows/), the object is refreshed across t ## How Commits Work{#how-commits-work} +In understanding commits, it is important to remember that each persistable object has two states: + +1. The state in the database where the values are shared with every other user of the app. This is the state you get when you retrieve an object which is not already in the app's memory. +2. The state in memory where values changed by the app can be seen. + +Non-persistable entities behave like the in-memory version of a persistable object. + ### 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 which is in memory, all changes to the database values are saved. Once it is committed, 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 or UPDATE on the database. For an object of a persistable entity, the database COMMIT is not performed until the microflow and any microflows from which it is called, complete. This means that, although a retrieve from the database by the end-user's app will see the updated version of the object, the updated object will not be seen globally by other end-users 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 distinction is that, in contrast to an explicit **Rollback object** call, which rolls back to the last Mendix commit, errors in a microflow can initiate a database rollback. If a microflow activity errors and has **Error handling** set to **Rollback** or **Custom with rollback**, the value of the object in the database is rolled back to the value it had at the last savepoint. 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, changes to the current values are 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 in memory – it is marked as changed. + 1. You perform a **Commit object(s)** activity which sends the changes to the database – the object in memory is marked as unchanged. + 1. An error occurs after the **Commit object(s)** has successfully sent changes to the database – 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 but it 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 version which is in memory, you will have to work around this behavior by creating a new object which contains the changes. + +* 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 in memory is new (that is, it has not been committed before you delete it), the delete will do nothing. However, the subsequent commit will write the object to 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. This means that: + +* Performing a commit on a non-persistable entity means that you cannot use a **Rollback object** activity to go back to the previous values +* If an error occurs in a microflow, and you have error handling which does a rollback, the values roll back to the state a the previous savepoint. ### Autocommit and Associated Objects {#autocommit-and-associated-objects} -When an object is committed through a default **Save** button, a commit activity, or web services, it always triggers the commit events. The platform also evaluates all associated objects. To guarantee data consistency, the platform may also autocommit associated objects. +When an object in memory is committed through a default **Save** button, a commit activity, or web services, it always triggers the commit events. The platform also evaluates all associated objects. To guarantee data consistency, the platform may also autocommit associated objects. An autocommit is an automatic commit from the platform, which is done to keep the domain model in sync. If your application ends up having autocommitted objects, then you will have a modeling error. Since an association is also a member of an object, the association is stored in the database as well. This means that if you create an order line inside an order and the order line is the parent of the association, when you commit the order line, the order is autocommitted. 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..53571350d32 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 @@ -10,15 +10,19 @@ This event can only be used in **Microflows**. ## Introduction -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/)). +An error event defines where a microflow will stop and throw a new error based on the error that occurred earlier. If you call a microflow, you may want to know whether any errors occurred within the microflow or not. + +When you use this event, it creates a new error with the same information as the original error. Because this is a new error, even if the error that occurred earlier was caught **without rollback**, 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. +## Example of Error Event + +In the example below, an error flow is defined when performing a Mendix commit. 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..ad082324a6b 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,7 +24,22 @@ There are four error handling options: * **Custom without rollback** * **Continue** -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. +In summary, the four error handling options do the following: + +| Option | Behavior | +| --- | --- | +| Rollback (default) | Reverts all changes, aborts the microflow, and returns the error to the calling microflow. | +| Custom with rollback | Reverts all changes and follows the error flow. | +| Custom without rollback | Keeps all changes made before the error and follows the error flow. | +| Continue | Keeps all changes and continues the microflow as if no error occurred. No error is logged or shown to the end-user. | + +Except for the **Rollback** option, which is the default, the other three are all custom error handling options. + +{{% alert color="info" %}} +If custom error handling is not implemented, the end-user will see a system error message when an error occurs. +{{% /alert %}} + +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" %}} In the error handling flow, you can include activities that update the database. Custom error handling does not affect newly created objects there, but if you try to update the status of an existing object that was created outside of this flow and that was rolled back because of custom error handling, then an error will occur. @@ -34,57 +49,61 @@ You can also include decisions in the error handling flow based on what you want It is important to know that when a microflow is started by an end-user (for example, by clicking a button), a savepoint is always created at the very beginning of the microflow. This is the case for **Microflow 1** in all the following examples. Knowing how savepoints are created and function is key to understanding how error handling works. -In the following subsections, we introduce the functionality of each error handling option using simple examples. +In the following subsections, we introduce the functionality of each error handling option using simple examples. In the examples we have an activity which generates the error. In a real app, however, an error can be generated by any activity. Any changes which happen within the activity which fails will not be applied (for example, if a [Call REST Service](/refguide/call-rest-action/) returns an error, any data the REST call retrieved will not be kept). The error handling only applies to activities which happened before the activity which errors. ### Error Handling - Default 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. +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 changes made in **Create Order** and in **Create Customer** are both reverted.), **Microflow 2** terminates, then **Microflow 1** terminates, the error is logged, and a system error message is shown to the end-user. + +{{% alert color="warning" %}} +After a rollback, previously committed objects remain marked as unchanged in memory, even though their database state has been reverted. 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 +#### Error Handling Which Ends with an End 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. +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-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-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. 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. +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. -#### Error Handling Which Ends with an End Event +#### 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 end 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-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-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. +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 a new error (with the same information as the original error) to terminate **Microflow 2**, and **Microflow 1** will terminate because there is no custom error handing there. ### Error Handling - Custom without Rollback Setting **Custom without rollback** will not stop data changes within the activity which failed from being rolled back. For example, if a [Call REST Service](/refguide/call-rest-action/) returns an error, any data the REST call retrieved will not be kept. -#### Error Handling Which Ends with an Error Event +#### Error Handling Which Ends with an End Event -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. +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-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-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 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. +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. Because the error flow ends with an end event, **Microflow 1** does not receive the error and continues to its end. Changes made in **Create Customer** are kept. -{{% alert color="info" %}} -Using custom error handling without rollback and ending the error handling flow with an error event here makes it possible to create a custom log message with details about the **NewOrder** (if you used error handling with rollback, the information about the **NewOrder** would not be available when creating the custom log message). However, after logging this message, everything will still be rolled back. -{{% /alert %}} +#### Error Handling Which Ends with an Error Event -#### Error Handling Which Ends with an End Event +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. -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-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." >}} -{{< 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" >}} +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 a new error (with the same information as the original error) to terminate **Microflow 2** and, because this error is not handled in **Microflow 1**, 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. -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. +{{% alert color="info" %}} +Using custom error handling without rollback and ending the error handling flow with an error event here makes it possible to create a custom log message with details about the **NewOrder** (if you used error handling with rollback, the information about the **NewOrder** would not be available when creating the custom log message). However, after logging this message, everything will still be rolled back as the error event is not handled in **Microflow 1**. +{{% /alert %}} ### Error Handling - Continue @@ -92,9 +111,9 @@ 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. +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, **Microflow 2** 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. Data changes within the activity which returns an error (for example, within an iteration of a [loop](/refguide/loop/)) will always be rolled back. It is only data changes outside the activity returning the error which will be kept. @@ -106,7 +125,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 +190,33 @@ 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 within your app happens in a transaction. By default either all activities 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. + +{{% alert color="info" %}} +This applies to data held within your app and its database. Calls to external systems will not be reversed if a transaction errors. You have to handle these cases specifically. +{{% /alert %}} + +### Isolated Changes + +While updating or creating your objects, you do not want other end-users to see updated 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 write lock on that object until the transaction/microflow ends. While the microflow is running, nothing else can write to 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 read 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 overwrite the changes committed first. +{{% /alert %}} + ## Read More * [Error Handling in Nanoflows](/refguide/error-handling-in-nanoflows/)