---
question: How do I cache a function on the cold tier?
kind: task
via_web: na
via_cli: na
via_python: available
via_api: na
via_config: na
ref_python: cache.cold
prereq: An in-engine module that lists `CacheService` in its dependencies. [Create a module](/user-guide/build/modules/creating-a-module) first.
next:
  - /capabilities/build/platform/choose-a-cache-backend
  - /capabilities/build/platform/cache-overview
---

# Cache a function

The cache wraps a function: a call with a key it has seen returns the stored result, any other call runs the function and stores what it returns. The decorator belongs to a tier: `cache.cold` is the durable tier, `cache.hot` the fast one.

## Web UI

Not applicable. Nexus does not wrap code. Super admins can see what a function cached under **System** › **Data** › **Cache**, and delete an entry so the next call computes it again. Use the Python steps on this page.

## CLI

Not applicable. There is no `abi` command for the cache. Use the Python steps on this page. The command list is the [CLI reference](/user-guide/reference/cli).

## Python

### Declare the service

```python title="src/my_module/__init__.py"
from naas_abi_core.module.Module import BaseModule, ModuleDependencies
from naas_abi_core.services.cache.CacheService import CacheService


class ABIModule(BaseModule):
    dependencies: ModuleDependencies = ModuleDependencies(
        modules=[],
        services=[CacheService],
    )
```

### Wrap the function

The first argument builds the key from the function's arguments. It receives only the arguments whose names it declares.

```python
from datetime import timedelta

from naas_abi_core.services.cache.CachePort import DataType

cache = self.engine.services.cache

@cache.cold(
    lambda company_id: f"my_module:company:{company_id}",
    cache_type=DataType.JSON,
    ttl=timedelta(hours=6),
)
def load_company(company_id: str) -> dict:
    ...  # the slow call
    return {"id": company_id}
```

`cache_type` is `DataType.TEXT`, `JSON`, `BINARY` or `PICKLE`, and must match what the function returns: `TEXT` takes a `str`, `BINARY` takes `bytes`. `ttl` is optional: without it an entry never goes stale. `@cache(...)` is the same as `@cache.cold(...)`. `@cache.hot(...)` needs a `hot` tier, or raises `ValueError`.

### Force a new computation

```python
company = load_company("acme", force_cache_refresh=True)
```

The wrapper runs the function and overwrites the entry whenever the call passes `force_cache_refresh`, whatever its value. Pass `auto_cache=False` to the decorator to read the cache but never write it.

### Read and write by hand

```python
from naas_abi_core.services.cache.CachePort import CacheNotFoundError

cache.set_json("my_module:company:acme", {"id": "acme"})   # cold tier
try:
    company = cache.get("my_module:company:acme", ttl=timedelta(hours=6))
except CacheNotFoundError:
    company = None
cache.delete("my_module:company:acme")                      # every tier
```

`get` reads hot, then cold, and raises `CacheNotFoundError` when no tier has a fresh entry. `exists(key)` returns a bool. `list_keys(prefix=..., limit=100)` returns a page of keys across the tiers; pass `page.next_after` as `after` for the next one.

## REST API

Not applicable. The System routes of the Nexus API read, write and delete cache entries, but no call wraps a function. Use the Python steps on this page.

## Config

Not applicable. `services.cache` picks the tiers, not what is cached. See [Choose a backend](/user-guide/build/platform/choose-a-cache-backend?via=config).

## Good to know

- **Same process.** The wrapper lives in the process that defined the function. The entries live in the tiers, so another process that wraps the same key reads them.
- **Stale is not deleted.** With a `ttl`, an older entry is computed again and overwritten on the next call. Until then it stays in the tier.
- **Type changes.** When a cached entry has another type than `cache_type`, the wrapper computes the value again and replaces the entry.
