v0.11.6

Services

Service Syntax

Constraints

Any class can be a service, but:

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

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

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 should be handled through a status in the return value.

(That's another reason why having an object as return value is a good practice.)

When a task fails due to a thrown exception, it will be automatically retried based on its retry policy.

Task timeout

The timeout defined the maximum duration of task execution.

By default, tasks do not have a timeout defined.

There are multiple ways to define a 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:

Task retries

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
tagsSet<String>tags of the task
taskIdStringid of the task
taskNameStringname of the task
workflowIdStringid of the workflow (may be null)
workflowNameStringname of the workflow (may be null)
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 from the thread having initiated the task execution, the getTimeoutInSeconds call or the getSecondsBeforeRetry 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.context.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: MyService
    class: com.company.services.MyServiceImpl
Previous
Service Workers

New version 0.11.2!