import { snippets } from "@/lib/generated/snippets";
import { Snippet } from "@/components/code";
import { Tabs, Callout } from "nextra/components";
import UniversalTabs from "@/components/UniversalTabs";

# Sticky Worker Assignment (Beta)

<Callout type="info">
  This feature is currently in beta and may be subject to change.
</Callout>

Sticky assignment is a task property that allows you to specify that all child tasks should be assigned to the same worker for the duration of its execution. This can be useful in situations like when you need to maintain expensive local memory state across multiple tasks in a workflow or ensure that certain tasks are processed by the same worker for consistency.

<Callout type="warning">
  This feature is only compatible with long lived workers, and not webhook
  workers.
</Callout>

## Setting Sticky Assignment

Sticky assignment is set on the task level by adding the `sticky` property to the task definition. When a task is marked as sticky, all steps within that task will be assigned to the same worker for the duration of the task execution.

<Callout type="warning">
  While sticky assignment can be useful in certain scenarios, it can also
  introduce potential bottlenecks if the assigned worker becomes unavailable, or
  if local state is not maintained when the job is picked up. Be sure to
  consider the implications of sticky assignment when designing your tasks and
  have a plan in place to handle local state issues.
</Callout>

There are two strategies for setting sticky assignment for [DAG](./dags.mdx) workflows:

- `SOFT`: All tasks in the workflow will attempt to be assigned to the same worker, but if that worker is unavailable, it will be assigned to another worker.
- `HARD`: All taks in the workflow will only be assigned to the same worker. If that worker is unavailable, the workflow run will not be assigned to another worker and will remain in a pending state until the original worker becomes available or timeout is reached. (See [Scheduling Timeouts](./timeouts.mdx#task-level-timeouts))

<UniversalTabs items={['Python', 'Typescript', 'Go', "Ruby"]}>
  <Tabs.Tab>

<Snippet src={snippets.python.sticky_workers.worker.sticky_worker} />

  </Tabs.Tab>
  <Tabs.Tab>

<Snippet src={snippets.typescript.sticky.workflow.sticky_task} />

  </Tabs.Tab>
  <Tabs.Tab>

<Snippet src={snippets.go.sticky_workers.main.sticky_dag} />
  </Tabs.Tab>
  <Tabs.Tab title="Ruby">
    <Snippet src={snippets.ruby.sticky_workers.worker.sticky_worker} />
  </Tabs.Tab>
</UniversalTabs>

In this example, the `sticky` property is set to `SOFT`, which means that the task will attempt to be assigned to the same worker for the duration of its execution. If the original worker is unavailable, the task will be assigned to another worker.

## Sticky Child Tasks

It is possible to spawn child tasks on the same worker as the parent task by setting the `sticky` property to `true` in the `run` method options. This can be useful when you need to maintain local state across multiple tasks or ensure that child tasks are processed by the same worker for consistency.

However, the child task must:

1. Specify a `sticky` strategy in the child task's definition
2. Be registered with the same worker as the parent task

If either condition is not met, an error will be thrown when the child task is spawned.

<UniversalTabs items={['Python', 'Typescript', 'Go', "Ruby"]} variant="hidden">
  <Tabs.Tab>

<Snippet src={snippets.python.sticky_workers.worker.sticky_child} />

  </Tabs.Tab>
  <Tabs.Tab>

<Snippet src={snippets.typescript.sticky.workflow.sticky_task} />

  </Tabs.Tab>
  <Tabs.Tab>

<Snippet src={snippets.go.sticky_workers.main.sticky_child} />

  </Tabs.Tab>
  <Tabs.Tab title="Ruby">
    <Snippet src={snippets.ruby.sticky_workers.worker.sticky_child} />
  </Tabs.Tab>
</UniversalTabs>
