New version 0.13.0!

v0.13.0

Services

Service Syntax

Constraints

Upon receiving a message instructing to execute a task, a service worker instantiates the service class, deserializes the parameters, executes the requested method and returns the serialized result:

  • The parameters and return value of a method used as task must be serializable.

If we chose to have several executions in parallel (concurrency > 1), they must not interfere with each other:

A method used as a task must be thread-safe.

If our method uses multi-threading, we should keep in mind that a task is considered completed when the method returns. Any exception occurring on another thread than the one that initiated the execution of the task will be ignored.

Recommendations

For an easier versioning of services, we recommend that:

Each service should be given a simple name through the @Name annotation.

Methods used as tasks should have:

  • one parameter of a dedicated type object
  • a return value of a dedicated type object

For example,

Task failure

Technical failures should be distinguished from non-technical failures:

  • a technical failure is triggered by an unexpected exception,
  • a non-technical failure is the inability to fulfill the task due to "business" reasons. For example:
    • a flight booking fails because no seats remain available
    • a bank wire fails because there is not enough money in the account

Non-technical failures are better handled through a status in the return value.(That's another reason why having an object as return value is a good practice.)

When an exception is thrown during a task execution, the task will be automatically retried based on its retry policy.

When a task execution run for longer than a defined timeout, the task will also automatically failed and retried.

Runtime timeout

We can set a maximum duration of task execution inside a Service worker, by defining a runtime timeout in the Service implementation. By default, tasks do not have a runtime timeout defined.

Since 0.12.0, the runtime timeout must be defined on the class implementation. When defined in the interface, a timeout represents the maximal duration of the task dispatched by workflows (including retries and transportation) before a timeout is thrown at workflow level.

There are multiple ways to define a runtime timeout:

The timeout policy used will be the first found in this order:

  1. Worker's configuration
  2. @Timeout method annotation
  3. @Timeout class annotation
  4. WithTimeout interface
  5. No timeout

WithTimeout interface

The WithTimeout interface requires a getTimeoutInSeconds method. When present, Infinitic will call this method to know which timeout to apply for all tasks of the service:

We can use the task context to personalize the timeout per task, for example:

@Timeout annotation

This annotation has a class implementing WithTimeout as parameter.

It can also be used as a method annotation to define a timeout on a specific task:

Or as a class annotation to define a timeout on all tasks:

Retries policy

Per default, all tasks have a truncated and randomized exponential backoff retry policy.

There are multiple ways to define another retry policy:

The retry policy used will be the first found in this order:

  1. Worker's configuration
  2. @Retry method annotation
  3. @Retry class annotation
  4. WithRetry interface
  5. Default retry policy

WithRetry interface

The WithRetry interface requires a getSecondsBeforeRetry method with 2 parameters:

  • retry: retry index (starting at 0)
  • exception: the exception that triggered the failure

When present, Infinitic will call this method if an exception was thrown. The result of this call will tell Infinitc how many seconds it should wait before retrying the task.

We can use the task context to personalize the delay before retry per task, for example:

@Retry annotation

@Retry annotation takes a class implementing WithRetry as parameter.

It can be used as a method annotation to define a retry policy on a specific task

Or as a class annotation to define a timeout on all tasks

Task context

In some cases, we want to know more about the context of the execution of a task.

io.infinitic.tasks.Task contains the following static properties:

NameTypeDescription
taskIdStringid of the task
taskNameStringname of the task (from the @Name annotation, or the method's name by default)
serviceNameStringname of the Service (from the @Name annotation, or the service's interface name by default)
workflowIdStringid of the workflow (if part of a workflow)
workflowNameStringname of the workflow (if part of a workflow)
tagsSet<String>tags of the task
retrySequenceIntegernumber of times the task was manually retried
retryIndexIntegernumber of times the task was automatically retried (reset to 0 after a manual retry)
lastErrorExecutionErrorif any, the error during the previous attempt
clientInfiniticClientan InfiniticClient that can be used inside the task

Those data are only accessible within:

  • the task execution
  • the getTimeoutInSeconds execution
  • the getSecondsBeforeRetry execution

from the thread that initiated the call.

RetrySequence is incremented when a task is manually retried:

Tasks retries

In tests, we can mock io.infinitic.tasks.TaskContext and inject it through Task.set(mockedTaskContext) before running a test that uses task's context.

@Name annotation

A task instance is internally described by both its full java name (package included) and the name of the method called.

We may want to avoid coupling this name with the underlying implementation, for example if we want to rename the class or method, or if we mix programming languages.

Infinitic provides a @Name annotation that let us declare explicitly the names that Infinitic should use internally. For example:

When using this annotation, the Service name setting in Service worker configuration file should be the one provided by the annotation:

services:
  - name: MyNewServiceName
    class: com.company.services.MyServiceImpl
Previous
Service Workers