Category Archives: Foundations

Saga Orchestration – A Copy-and-Paste Implementation

Requirements:

  • BPMN activities:
    • service task activity
    • fork and split gateway for a parallel execution of activities
    • compensation activity
  • compensation logics
    • automatic trigger of compensation on exception
    • reverse order on compensation
  • store, read, and pass variables
  • save point after each activity for fault tolerance (e.g., DB or file system)
  • visualization of transactions for debugging and manual compensation purposes

Scenario:

  1. create pending order
  2. reduce inventory
  3. fulfil payment
  4. publish order

Start a saga instance:

// start saga instance from saga model "order creation"
orderCreationSaga = sagaFactory.startNewInstanceFromSagaModel("order creation")
orderId = orderCreationSaga.getVariables().getOrWait("orderId", 500, Millis)
orderCreationSaga.waitForCompletion(3_000, Millis)

The task to execute a single saga step:

	public SagaStepRunnable(SagaInstance sagaInstance, SagaActivity sagaActivity) {
		this.sagaInstance = sagaInstance;
		this.sagaActivity = sagaActivity;
	}

	@Override
	public void run() {
		SagaLog sagaLog = sagaInstance.getSagaLog();

		try {
			sagaActivity.execute(sagaInstance.getVariables());
			// if the thread crashes at this point, the saga restarts after the last successful activity.
			// hence, all activities (local and remote) must be idempotent.
			sagaLog.storeSuccessEventFor(sagaActivity, sagaInstance.getVariables());
		} catch (Exception e) {
			sagaLog.storeFailureEventFor(sagaActivity, sagaInstance.getVariables(), e);
			sagaInstance.switchToCompensationMode();
		}

		sagaInstance.scheduleNextActivities();
	}

Questions:

  • necessary to sync “entity creation for order” with “current variables” and “success event”?
  • remote AND local services must be idempotent?

Transactional Outbox Implementations

The transactional outbox pattern allows to update the state of a database and to send a message to a message broker in one single transaction. In this way, either both or none of these actions happen. In other words: they are executed atomically. Details of this pattern can be found here: https://microservices.io/patterns/data/transactional-outbox.html.

There are numerous approaches to implement this pattern. In the following, we discuss those approaches that are most reasonable to me.

Polling Publisher

Using a Scheduled Database Poller

Polling the database in a regular time intervall is the straight-forward implementation of the “Polling Publisher”.

Scheduled Database Poller with Java and Spring:

@Scheduled
void pollDatabase() {
   Optional<OutboxEvent> event = this.repository.findFirstByTimestampByOrderAsc();
   event.ifPresent(this.eventService::send);
}

EventService with Java and Spring:

@Transactional
@Retryable(..)
public void send(OutboxEvent event) {
   this.kafkaTemplate.send(..);

   this.repository.deleteById(event.getId());
}

Attention must be paid to a multi-instance operation. If multiple instances share the same database, they also share the same outbox table. As a consequence, they interfere with each other when processing outbox entries.

Advantages: This approach is easy to implement and requires no special infrastructure.

Disadvantages: The scheduler performs polling and thus stresses the database unnecessarily. Moreover, running multiple instances requires a more complex implementation to keep the order of events.

Requirements: Apparently, this approach requires a database table to store the outbox events. Moreover, it requires a poller – either executed by a thread or by a dedicated application.

What about using an in-memory queue as optimization to avoid polling?

Unfortunately, this approach either looses events or falls back to polling depending on whether the notification is passed after or within the transaction.

Using a Workflow Engine

Bernd Ruecker proposes to use a workflow engine in order to implement the transactional outbox pattern. The corresponding process model is illustrated in the following figure.

The underlying workflow engine remembers the position in the process model, so that it does not execute tasks again which were already executed completely. In this way, the workflow engine resumes with the task that has not been finished and should be processed next according to the process model.

Hence, If the application crashes before completing the database operation task, the process instance resumes at this point and re-executes the database operation task. If the application crashes before completing the notification task, the process instance resumes at this point and re-executes the notification task.

Strictly speaking, this approach does not represent the pattern anymore, because it does not make use of an outbox table. Nonetheless, it puts the pattern’s underlying idea into practice.

Advantages: This approach requires neither a dedicated outbox table, nor special infrastructure or a custom scheduler. Moreover, we can make use of the monitoring and operations capabilities of the workflow engine tooling to debug pending tasks.

Disadvantages: The integrated job scheduler of the workflow engine still performs polling. Moreover, this approach does not keep the order of events.

Requirements: Apparently, this approach requires a workflow engine. If the engine and the process model should be hidden from the application developer, a transactional outbox API is recommended that encapsulates these implementation details.

Why does this approach work?

The alternative approaches remember the intent to send an event by storing an entry in the outbox table. In contrast, this approach remembers the intent by storing the current position and the event’s data in the process instance. Of course, this data is also represented by database tables. However, these tables are managed automatically by the workflow engine, and not by the application developer.

Transaction Log Tailing

TODO no polling

Scheduling Tasks/Jobs with Spring (Boot)

Static Scheduling

Spring offers the annotation @Scheduled to define a task and its corresponding scheduling, e.g., execute this method every 5 minutes. The annotation saves you a great deal of work: in the background, it creates or looks up a scheduler, creates a task which invokes your method, and passes the task to the scheduler with your scheduling arguments (here: every 5 minutes).

Scheduling Parameters

The annotation @Scheduled allows to specify a fixed delay (in ms), a fixed rate (in ms), or a more flexible cron expression if the first two options are not expressive enough for your use case. The following code snippet shows an implementation of our “every 5 seconds”-example from above:

@Component // or any subtype like @Service
public class AnyComponent {

    private static final Logger log = LoggerFactory.getLogger(AnyComponent.class);

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    @Scheduled(fixedRate = 5000)
    public void reportCurrentTime() { // visibility is irrelevant: even private is possible
        log.info("The time is now {}", dateFormat.format(new Date()));
    }
}
Task Method Requirements

The method, which is annotated with @Scheduled, must fulfill the following requirements:

  • The enclosing class of the method must be a Spring component. That is, the class must be annotated with the annotation @Component or an annotation which includes @Component like @Service, for example.
  • The method must be void.
  • The method must have no parameters.
  • The method may be private.
Read Parameter Values from a Properties File

If you do not want to specify the delay or the rate directly in the code, you can read it from the configuration context. For this purpose, Spring provides the parameters fixedDelayString and fixedRateString. The following example reads the seconds of the delay from the configuration key my.property.fixed.delay.seconds. If the value is not available or invalid, a default value of 60 is used. Since the delay expects a value in milliseconds, 000 is appended to the read value.

// with a default value of 60
@Scheduled(fixedDelayString = "${my.property.fixed.delay.seconds:60}000")

Further examples:

// without a default value
@Scheduled(fixedDelayString = "${my.property.fixed.delay.seconds}000")
// without appending
@Scheduled(fixedDelayString = "${my.property.fixed.delay.milliseconds}")
// hard-coded value as string (not recommended due to missing static type checking)
@Scheduled(fixedDelayString = "7000")

With these *String variants, you can define the delay or, respectively, the rate from aproperties file. Note that the value is read only once at startup time. Thus, this approach is still static. You cannot change the value at runtime. We refer to the Javadoc of @Scheduled for more detailed information on the parameters.

Dynamic Scheduling

If you need to read your scheduling arguments dynamically, that is, at runtime, then you @Scheduled is not sufficient. Instead, your can use Spring’s interface TaskScheduler. Declare a field with this type and annotate it with Spring’s @Autowired annotation. Then, you can pass a dynamically created task to the scheduler and specify whether it should be executed once or repeatable with a fixed delay or at a fixed rate. We refer to the documentation of the TaskScheduler for more information.

Sources

Join Points and their Pointcuts in AspectJ

In the following list, we name each join point with its corresponding pointcut because it was initially not apparent to me how to capsure all calls (or executions). You cannot use one single * wildcard to match all method and constructor calls. You need to define a composite pointcut which capsures on the one hand all method calls and on the other hand all constructor calls. This differentiation by AspectJ is not directly reflected by the documentation, but only by AspectJ’s different pattern syntax for call (or execution). To capsure method calls, you need to use MethodPattern. To capsure constructor calls, you need to use ConstructorPattern. The complete syntax for all patterns of AspectJ can be found here.

  • method-calls: call(MethodPattern)
  • method-execution: execution(MethodPattern)
  • ctor-calls: call(ConstructorPattern)
  • ctor-execution: execution(ConstructorPattern)
  • static init: staticinitialization(TypePattern)
  • preinit: preinitialization(ConstructorPattern)
  • init: initialization(ConstructorPattern)
  • field-reference (a.k.a. field-read): get(FieldPattern)
  • field-set (a.k.a. field-write): set(FieldPattern)
  • handler: handler(TypePattern)
  • advice-execution: adviceexecution()

Aspect-oriented Programming with AspectJ

AspectJ is an open-source framework that allows you to write Java code following the aspect-oriented programming (AOP) paradigm. AOP provides an alternative way of programming in the following situation:

Let us assume you want to implement some functionality which is required at multiple locations in your application’s source code. If we follow the object-oriented programming (OOP) paradigm, we would proceed as follows. First, we declare a new class. Then, within this class, we declare a new method which implements the required functionality. If necessary, we add some associated fields. Afterwards, we place an invocation of the method (together with the class instantiation) at each of the required locations in our source code.

If we follow the AOP paradigm, we would proceed as follows. First, we declare a new aspect which is similar to a class. Then, within this aspect, we declare a new advice which corresponds to the method from above. If necessary, we add some associated fields to the aspect. So far, we have proceeded exactly as if we had followed the OOP paradigm – only with different terms. But now, we do not place an invocation of the advice at each of the required locations. Instead, we write down these locations in our aspect and pass it to an additional compiler – the AOP compiler. This AOP compiler reads in the aspect and automatically places an invocation of the advice at each of the designated locations. Thereby, it also handles the instantiation of the aspect. The set of our locations is called a pointcut and is associated with our advice. In this way, the AOP compiler knows which code it should place at which locations. However, most AOP compilers cannot insert advices at arbitrary locations, but only at well-defined locations. For example, the AspectJ compiler can insert advices at method calls and field accesses, but not at loops or if-statements. Such well-defined locations are called joinpoints. Thus, a pointcut is not a set of arbitrary locations, but a set of joinpoints.

Hence, an aspect is not instantiated and accessed directly from within your source code. Instead, it is automatically inserted into your source code by the AOP compiler. For this reason, we do not term it class, but aspect. In summary, the AOP paradigm allows to insert some functionality at multiple locations in your application without the effort of touching the source code at each individual location by hand. In this way, it provides a clean modularization of crosscutting concerns and thus serves as a complement to the OOP paradigm.

AOP terms in short:

  • A jointpoint is a location in code at which the AOP compiler is able to automatically insert new code either before, after, or around (i.e., before and after). Valid joinpoints in AspectJ are, for example, method declarations, method calls, and field read/write accesses. So far, AspectJ does not support inserting code at loops and if-statements. So, AspectJ does not consider these constructs as joinpoints.
  • A pointcut is a set of joinpoints at which we want to insert the same new code. This set of joinpoints is not described as an enumeration of each and every individual joinpoint, but instead as a single compact expression similar to a regular expression.
  • An advice contains the new code which we want to insert at multiple joinpoints in our application’s source code.
  • An aspect contains one or more advices and their associated pointcuts. By default, AspectJ creates and uses one instance per aspect. So, you can consider an aspect as singleton. However, you can change this behavior to a per-object or per-joinpoint base (see aspect instantiation for more details).

For more information, we refer to the official Getting Started and the mapping of join points and pointcuts.

What is a UML Software Component?

Introduction

Since I work at the Kiel University, I teach UML to the students in the 3rd and 4th semester of computer science. At the latest when they need to work out a UML component diagram and a corresponding deployment diagram, the following questions arise:

What is a UML software component? What is the difference between a class, a package, and a component?

For this reason, we address these questions in this post. First, we look at some definitions to distinguish a component from other UML entites.

Component vs. (Class, Package, and Library)

Let’s start with the most important message of this post: Consider a component as a composition of classes, packages, and sub components that is accessible only by other components and only via interfaces.

TODO

Communication within and between components

TODO UML notation

Example Component Diagram

When creating a component diagram, remember that it must depict additional semantics compared to all other diagram types such as class and package diagrams. Otherwise a component diagram is useless and can be thrown away.

TODO diagram with components (android, server, db) and sub components: A3, User, Role, ORM, …

Example Component Representations in Java

Since the Java programming language does not provide the abstraction of components, we need to represent it with the means Java offers us.
Hence, we discuss the three following representations of a component possible in the Java universe.

  1. The class-based approach
  2. The package-based approach
  3. The library-based approach

Due to Java’s direct support for interfaces, we represent a component’s interface by one or more Java interfaces.

The class-based approach

In this approach, we represent a component by a class that implements all interfaces the corresponding component should provide.

 

The package-based approach

TODO

The library-based approach

TODO