Skip to main content

Routing Tasks (Task-to-domain)

Typically, for each configured task type - Conductor server will maintain a queue and will distribute task work to all the workers connected to the server, as shown in this diagram:

Task to domain example workflow

Conductor supports a feature where the same task can be routed to a different set of workers based on a concept called as Task-to-domain. This value is supplied to the workflow when triggered, and if present, it routes the tasks in a different way than usual. This is illustrated as shown below.

Task to domain example workflow

Here - task type send_email has two additional sets of worker instances listening to specific domain-based tasks. Domains are nothing but a label that connects the task work to the worker instance. For example, all the tasks with domain dedicated_for_app_x are sent to the workers configured with domain dedicated_for_app_x.

tip

The Task-to-domain is the concept of limiting the task execution only to a specific worker via domain mapping. The domain name can be an arbitrary string.

This feature can be really useful in the following scenarios:

  • Ensuring that the work is routed to a worker with the appropriate permissions
  • Load balancing or prioritizing some tasks with a set of dedicated workers
  • Implementing unique behaviors by domain
  • Debugging a task with a worker deployed on a local machine or a worker running a different version of the code than the regular one

All of these cases can also be done using a unique task name, and the domain is just an alternative way. Instead of creating new tasks, we can use the same task name and still have custom routing based on the domain.

Using Task-to-domain

To successfully route a task by domain:

  1. Configure your workers to start polling for work that is tagged by the domain.
  2. When triggering the workflow, ensure the taskToDomain map is set to the right mapping values.

Configuring Workers with Domain

Let's configure the workers with a domain label called test. Every worker polling for taskName will use test as domain.

The following table shows the order of precedence when initializing the task domain for a worker. If a system property is set according to the table below, it takes priority over the initialization of the taskToDomain map or passing the domain as an argument when using annotations. If ${TASK_NAME} is replaced by all in the system property name, then all workers will pickup that task domain.
DescriptionPropertyNameExample
System property by taskNameconductor.worker.${TASK_NAME}.domainconductor.worker.taskName.domain=test
System property for all workersconductor.worker.all.domainconductor.worker.all.domain=test
Class TaskRunner constructor paramtaskToDomaintaskToDomain=Map.of("taskName", "test")
Annotation @WorkerTask constructor paramdomain@WorkerTask(value="taskName", domain="test")

Code example for TaskRunner:

Map<String, String> taskToDomains = new HashMap<>();
taskToDomains.put("taskName", "test");
Map<String, Integer> taskThreadCount = new HashMap<>();

TaskRunnerConfigurer.Builder builder = new TaskRunnerConfigurer.Builder(taskClient, workers);
TaskRunnerConfigurer taskRunner = builder.withTaskToDomain(taskToDomains).build();


Code example for @WorkerTask:

@WorkerTask(value="taskName", domain="test")
public TaskResult sendAnnotatedTaskDomain(Task task) {
TaskResult result = new TaskResult(task);
// Populate result here
return result;
}

View the complete code here.

Specifying Domain while Invoking Workflow

When we start/run a workflow, we can specify which tasks must run on which domains. To run the workflow with tasks routed to the domain-based worker, we can specify the following task-to-domain mapping:

{ 
"task_x": "test"
}

While running the workflow, you can provide the task-to-domain mapping as shown in the following image:

Task To Domain mapping while invoking workflows

Fallback Task-to-domain

Another feature of domains is that you can specify a fallback domain. The concept is simple as follows:

{
"task_x": "test,fallback,NO_DOMAIN"
}

Conductor will try to route to task workers with domain test if available, and if not, will try domain fallback and subsequently to workers with no domain.

tip

NO_DOMAIN is a keyword that means workers with no domain.

In terms of the order of domains:

  • If NO_DOMAIN is provided as the last token in the list of domains, then no domain is set for the tasks.
  • Otherwise, the task will be added to the last inactive domain in the list of domains, hoping that workers will soon be available for that domain.

Also, a * token can be used to apply domains for all tasks. This can be overridden by providing task-specific mappings along with *.

In this example,

"taskToDomain": {
"*": "mydomain",
"task-a": "NO_DOMAIN",
"task-b": "abc, NO_DOMAIN",
"task-c": "someInactiveDomain1, someInactiveDomain2"
}
  • Task task-a is put in the default queue (no domain).
  • Task task-b is put in the abc domain, if available, or in default otherwise.
  • Task task-c is put in someInactiveDomain2, even though workers are unavailable.
  • All other tasks in this workflow are put in mydomain.
note
  • The "fallback" domain strings can only be used when starting the workflow. When polling from the client, only one domain is used.
  • The NO_DOMAIN token should be used last.