---
title: "Activity caching: keys, storage, and expiry"
description: "Explains how to declare @activity cached functions, choose keys and storage, set expiry and retries, and clear entries."
lang: en
status: public-preview
lastUpdated: 2026-10-21
url: https://duale.ai/en/docs/sdk/activity-caching
---

## AI-generated summary

This page covers caching async function results with @activity, including key management, storage selection, retry and timeout settings, and cache clearing.

- Caching eligibility and safety criteria.
- Declaring a cached function with @activity.
- Key derivation and invalidation strategy.
- Storage selection with Redis or SQLite.
- Retry, timeout, and cache clearing controls.

Summaries were generated by AI.

---

Use `@activity` to reuse the result of an async function that your application calls, such as a slow lookup. The SDK
stores the JSON-serializable result and returns it for the same arguments until the entry expires. `@activity` runs
only in your process and does not publish a Tool.

## Decide what to cache

Cache a result that stays correct for its whole lifetime and that your application can safely reuse. A cache is not
an external Effect ledger: do not rely on it to stop an external action from running twice. For a state-changing
operation, record the attempt in your own durable store, as [Secure
integration](https://duale.ai/en/docs/security/secure-integration.md#control-duplicate-attempts-and-uncertain-outcomes) describes.

## Declare a cached function

This excerpt runs inside an open SDK context:

```python
from datetime import timedelta

from dualeai import activity

@activity(sdk=sdk, cache_ttl=timedelta(minutes=10), max_retries=0)
async def normalize_sku(sku: str) -> str:
    return sku.strip().upper()

normalized = await normalize_sku(" sample-001 ")
```

The same arguments can reuse a cached result. If you omit `cache_ttl` or set it to `None`, an entry lives for one
hour. By default, the SDK varies each lifetime by up to 10% in either direction to spread expiry times.

**Check.** Call the function twice with the same arguments. The second call returns the stored result without running
the function body again.

## Choose keys and invalidate them

The key depends on the function name and the `repr()` of its arguments, not on the function body. Use distinct
function names and stable arguments. Include a version argument, or clear the cache, when a code change makes old
results invalid. Do not rely on caching a `None` return value: the SDK also uses `None` to mean that no entry exists.

## Choose the storage

The SDK opens its cache on first use. It tries Redis, then falls back to SQLite after a connection failure. The cache
keys carry a prefix derived from the original API token, not from `DUALEAI_TENANT_ID`; replacing that token selects a
new prefix. Protect the storage for the data your functions return. [SDK
configuration](https://duale.ai/en/docs/sdk/configuration.md#environment-variables) lists the Redis and SQLite settings.

To choose the storage explicitly, import `CacheConfig` and `create_cache_backend` from `dualeai.cache` and assign the
created cache to `sdk.cache` before the first activity. The factory's `tenant_id` argument is the prefix for cache keys;
it does not configure Platform access. This excerpt chooses SQLite directly and turns off lifetime variation:

```python
from dualeai.cache import CacheConfig, create_cache_backend

sdk.cache = await create_cache_backend(
    tenant_id="my-application-cache",
    redis_url=None,
    sqlite_path="activity-cache.db",
    config=CacheConfig(ttl_jitter_enabled=False),
)
```

The SDK closes the assigned cache during cleanup. Choose a separate key prefix for data that must not share cached
results.

## Set retries and the timeout

`max_retries` sets how many extra attempts follow an ordinary exception. It defaults to three. Enable retries only when
repeating the operation is safe.

The `job_timeout` argument of `DualeAISDK` limits the wait for scheduled activity work, including the time it waits
to run and its retries. Cache reads, cache writes, and any wait to enter a full queue are outside that timer, so the
whole call can take longer. It is separate from a Task's absolute `deadline`. Blocking synchronous work on the event loop can also delay
timeout handling.

When that wait exceeds `job_timeout`, the call raises the built-in `TimeoutError`. Treat any external Effect of the
function as uncertain: cancellation cannot roll back an action that already completed.

## Clear and expire entries

| Method                              | Effect                                                           |
| ----------------------------------- | ---------------------------------------------------------------- |
| `await sdk.clear_cache()`           | Delete all cached activity results under the current key prefix. |
| `await sdk.cleanup_expired_cache()` | Remove expired entries and return the number removed.            |

The lower-level `sdk.execute_activity(func, cache_ttl, max_retries, *args, **kwargs)` also accepts synchronous
functions, which run on the event loop; offload blocking work yourself. Unlike the decorator, `cache_ttl=None` on this
method stores the entry without expiry.

[Decorators](https://duale.ai/en/docs/sdk/reference.md#decorators-tool-and-activity) gives the `@activity` signature, and
[BackpressureConfig](https://duale.ai/en/docs/sdk/reference.md#backpressureconfig) sets how many activities run and wait at once.

## Related content

- [Context continuity: continuations, recall, and retention](https://duale.ai/en/docs/agent-harness/context-continuity.md)
- [Data protection: stored copies, Task reads, and data paths](https://duale.ai/en/docs/security/data-protection.md)
- [Code Execution: run bounds, isolation, and safety](https://duale.ai/en/docs/agent-harness/code-execution.md)
- [AI agent use cases: what each team hands over](https://duale.ai/en/use-cases.md)
- [Task execution: work division, deadline, and Stop](https://duale.ai/en/docs/agent-harness/executors.md)
- [Agent Identity credentials and lifecycle](https://duale.ai/en/docs/identity/credentials-and-lifecycle.md)

---

## Sitemap

See the full [Markdown sitemap](https://duale.ai/sitemap.md) for all pages.
