Files
baker/docs/archive/process-execution.md
Francisco Arámburo 75691cd686 Docs 3.0 (#256)
* Initial structure for the documentation of Baker 3

* Added the examples module and started working on the design a recipe section

* continued work on documentation

* More work on documentation

* Worked on the documentation

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* documentation work

* example work

* Documentation work

* Example app work

* Example app work

* Example web app done in Scala

* Documentation work

* Gatling setup

* Work on example app metrics

* Grafana setup

* Loadtesting mods

* Documentation work

* Example app memory dump work

* Load testing work

* proof read
2019-08-14 14:29:36 +02:00

8.2 KiB

Process Execution

Recap

On the runtime page we discussed how you could initialize Baker and add your recipe to it.

Lets summarize what we have done so far.

// To execute a recipe you will need a Baker instance
// This should be the same instance as you used to register your Recipe
val baker: Baker = ???

// The recipe id was given to us when we add our Recipe to Baker
val recipeId: String = ???
// To execute a recipe you will need a Baker instance
// This should be the same instance as you used to register your Recipe
JBaker baker = null; //

// The recipe id was given to us when we add our Recipe to Baker
String recipeId = "...";

Create a process instance

Given a valid recipe id we can now execute a Recipe.

Or in other words we can create a process instance.

// Assuming you have a compiled recipe
val recipe: CompiledRecipe = ???

// A unique identifier ment to distinguish this process from other process instances
val recipeInstanceId = "a-unique-process-id"

// Tell Baker that we want to create a new process for a certain recipe.
baker.bake(recipe.recipeId, recipeInstanceId)
// Assuming you have a compiled recipe
CompiledRecipe recipe = null; //

// A unique identifier ment to distinguish this process from other process instances
String recipeInstanceId = "a-unique-process-id";

// Tell Baker that we want to create a new process for a certain recipe.
baker.bake(recipe.recipeId(), recipeInstanceId);

Providing a sensory event

In our webshop example the first events that can happen are OrderPlaced, PaymentMade and CustomerInfoReceived.

These are so called sensory events since they are not the result of an interaction but must be provided by the user of Baker.

// The CustomerInfoReceived and OrderPlaced events require some data (Ingredients)
val customerInfo = CustomerInfo("John", "Elm. Street", "johndoe@example.com")
val order = "123";

// Lets produce the `OrderPlaced` and `CustomerInfoReceived` sensory Events.
baker.processEvent(recipeInstanceId, CustomerInfoRecived(customerInfo))
baker.processEvent(recipeInstanceId, OrderPlaced(order))
// The CustomerInfoReceived and OrderPlaced events require some data (Ingredients)
CustomerInfo customerInfo = new CustomerInfo("John", "Elm. Street", "johndoe@example.com");
String order = "123";

// Lets produce the `OrderPlaced` and `CustomerInfoReceived` sensory Events.
baker.processEvent(recipeInstanceId, new CustomerInfoReceived(customerInfo));
baker.processEvent(recipeInstanceId, new OrderPlaced(order));

When receiving events Baker will check which Interactions have all the required Ingredients and Events met.

It will execute those interactions.

Those interactions will raise more events.

For more information about the exact execution sementics see here.

Correlation id

Optionally you may provide a correlation id with an event. The purpose of this identifier is idempotent event delivery.

When sending the same event correlation id multiple times, only the first will be processed.

This can be applied to the OrderPlaced event for example.

val orderId = "a unique order id"

val statusA: SensoryEventStatus = baker.processEvent(recipeInstanceId, new OrderPlaced(order), orderId)
val statusB: SensoryEventStatus = baker.processEvent(recipeInstanceId, new OrderPlaced(order), orderId)

// statusA == Received
// statusB == AlreadyReceived

String orderId = "a unique order id";

SensoryEventStatus statusA = baker.processEvent(recipeInstanceId, new OrderPlaced(order), orderId);
SensoryEventStatus statusB = baker.processEvent(recipeInstanceId, new OrderPlaced(order), orderId);

// statusA == Received
// statusB == AlreadyReceived

Sensory event status

In response to receiving a sensory event Baker returns a status code indicating how it processed it.

Status Description
Received The event was received normally
AlreadyReceived An event with the same correlation id was already received
ProcessDeleted The process instance was deleted
ReceivePeriodExpired The receive period for the process instance has passed
FiringLimitMet The firing limit for the event was met

Incident resolving

It is possible that during execution a process instance becomes blocked.

This can happen either because it is directly blocked by a exception or that the retry strategy was exhuasted.

At this point it is possible to resolve the blocked interaction in 2 ways.

1. Force retry

For example, the retry strategy for "SendInvoice" was exhausted after a few hours or retrying.

However, we checked and know the "invoice" service is up again and want to continue to process for our customer.

baker.retryInteraction(recipeInstanceId, "SendInvoice")
baker.retryInteraction(recipeInstanceId, "SendInvoice");

2. Specify the interaction output

For example, the "ShipGoods" backend service is not idempotent and failed with an exception. It is not retried but blocked.

However, we checked and know the goods were actually shipped and want to continue the process for our customer.

baker.resolveInteraction(recipeInstanceId, "ShipGoods", GoodsShipped("some goods"))
baker.resolveInteraction(recipeInstanceId, "ShipGoods", new GoodsShipped("some goods"));

Note that the event provided SHOULD NOT include any event or ingredient renames specified for the interaction.

State inquiry

During runtime it is often useful to inquire on the state of a process instance.

Visualize process instance state

We can use the visualizer to see what Baker has done with the given events.

val dotRepresentation: String = baker.getVisualState(recipeInstanceId)
String dotRepresentation = baker.getVisualState(recipeInstanceId);

!!! hint "Did you know?!" You can ask baker for a visual representation of a certain process. That way you can see which Events were provided and which Interaction were run. See the visualization page how to create a visual graph.

The image shows us that the ValidateOrder Interaction is colored green which means that the Interaction has been exucuted. Baker was able to exeucte the Interaction because all required Events and Ingredients where provided. The ValidateOrder Interaction in turn produced the Valid event.

We can also see that the ManufactureGoods Interaction is still purple, meaning that it has not been executed. This is correct because ManufactureGoods requires an additional event called PaymentMade.

Events

We can ask Baker for a list of all the events for our process.

Using this list we can check if the InvoiceWasSend event was produced.

// Get all events that have happend for this process instance
val events: Seq[RuntimeEvent] = baker.getEvents(recipeInstanceId)
if (events.exists(_.name == "InvoiceWasSend"))
    // Yes the invoice was send!
// Get all events that have happend for this process instance
EventList events = baker.getEvents(recipeInstanceId);
if (events.hasEventOccurred(InvoiceWasSend.class))
    // Yes the invoice was send!

Ingredients

Sometimes it is useful to know what the ingredient values are accumulated for a process instance.

For example, you might want to know the value of the trackingId.

// Get all ingredients that are accumulated for a process instance
val ingredients: Map[String, Value] = baker.getIngredients(recipeInstanceId)

val trackingId: String = ingredients("trackingId").as[String]
// Get all ingredients that are accumulated for a process instance
Map<String, Value> ingredients = baker.getIngredients(recipeInstanceId);

String trackingId = ingredients.get("trackingId").as(String.class);