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

# Task Eviction

Since durable tasks are often waiting for sleeps, events, or child runs, they spend much of their time inactive. When this happens, the durable task no longer needs to hold a slot on the worker until the thing it's waiting for completes. Hatchet has the option to **evict** durable tasks from workers when they're in one of these waiting states, in order to free up slots on the worker. This allows the worker to pick up additional durable tasks, and then resume the original one from where it left off when the wait is satisfied, without keeping the idle task around.

<Callout type="info">
  It's important to note that eviction is a fundamental difference between
  durable and non-durable tasks. Only durable tasks can be evicted from and
  restored on workers, as opposed to non-durable tasks, which will hold slots
  throughout the entirety of their execution, even while they're inactive.
</Callout>

### Configuring Eviction Policies

Every durable task can configure an **eviction policy**, which tells Hatchet how to respond when that task hits a wait. There are a few configuration options available to you to choose from:

1. You can set a TTL, which tells Hatchet that the durable task should only be evicted after some amount of uninterrupted time has been spent waiting.
2. You can enable or disable **capacity-based eviction**, which allows Hatchet to optimistically evict the durable task if the worker is running out of slots and wants to pick up a new durable task run, but would be unable to if the waiting task could not be evicted.
3. Finally, you can provide a **priority**, which tells Hatchet what order candidate durable tasks should be evicted in (lower priority values will be evicted first when choosing between multiple candidate durable tasks to evict).

Start by declaring a policy:

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

<Snippet src={snippets.python.durable_eviction.worker.eviction_policy} />

</Tabs.Tab>
<Tabs.Tab title="Typescript">

<Snippet src={snippets.typescript.durable_eviction.workflow.eviction_policy} />

</Tabs.Tab>
<Tabs.Tab title="Go">

<Snippet src={snippets.go.durable.eviction.main.eviction_policy} />

</Tabs.Tab>
<Tabs.Tab title="Ruby">

<Snippet src={snippets.ruby.durable_eviction.worker.eviction_policy} />

</Tabs.Tab>
</UniversalTabs>

Then attach it to a durable task. Any time the task enters a wait (sleep, event wait, child spawn) the policy's TTL and capacity-eviction settings are honored:

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

<Snippet src={snippets.python.durable_eviction.worker.evictable_sleep} />

</Tabs.Tab>
<Tabs.Tab title="Typescript">

<Snippet src={snippets.typescript.durable_eviction.workflow.evictable_sleep} />

</Tabs.Tab>
<Tabs.Tab title="Go">

<Snippet src={snippets.go.durable.eviction.main.evictable_sleep} />

</Tabs.Tab>
<Tabs.Tab title="Ruby">

<Snippet src={snippets.ruby.durable_eviction.worker.evictable_sleep} />

</Tabs.Tab>
</UniversalTabs>

To opt a durable task out of eviction entirely, set `allowCapacityEviction` to `false` and leave the TTL unset. The task will hold its slot through waits:

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

<Snippet src={snippets.python.durable_eviction.worker.non_evictable_sleep} />

</Tabs.Tab>
<Tabs.Tab title="Typescript">

<Snippet
  src={snippets.typescript.durable_eviction.workflow.non_evictable_sleep}
/>

</Tabs.Tab>
<Tabs.Tab title="Go">

<Snippet src={snippets.go.durable.eviction.main.non_evictable_sleep} />

</Tabs.Tab>
<Tabs.Tab title="Ruby">

<Snippet src={snippets.ruby.durable_eviction.worker.non_evictable_sleep} />

</Tabs.Tab>
</UniversalTabs>

### Task Resumption

Hatchet will **resume** a task in an evicted state when the thing it was waiting for is satisfied. When this happens, the durable task is re-triggered on the worker, and its event log is replayed up to the checkpoint where it left off, at which point it continues making progress.

<Callout type="warning">
  Since durable tasks can be evicted or resumed, it's important to make sure
  that a durable task doesn't perform any operations that you wouldn't want to
  occur multiple times. For instance, non-idempotent writes to a database,
  sending emails, or computationally expensive work are generally not good
  candidates for the kinds of things to do in a durable task, since that logic
  will be rerun every time the task is resumed.
</Callout>
