# How the Cache Controller Works in Music Assistant: Implementation and API Guide

> Explore the Music Assistant cache controller's functionality and implementation. Learn how this JSON-based key-value store uses SQLite for async operations, with automatic expiration and bypass.

- Repository: [Music Assistant/server](https://github.com/music-assistant/server)
- Tags: internals
- Published: 2026-06-16

---

**The Cache Controller in Music Assistant is a JSON-based key-value store that uses SQLite for persistence, providing async methods for get/set/delete operations, automatic expiration handling, and a bypass mechanism for forced refresh operations.**

The cache controller is a core component of the `music-assistant/server` repository, providing a lightweight, JSON-based key-value store for the entire Music Assistant application. Located in [`music_assistant/controllers/cache/controller.py`](https://github.com/music-assistant/server/blob/main/music_assistant/controllers/cache/controller.py), this async-friendly caching layer centralizes data persistence, expiration management, and schema migration to ensure optimal performance across the platform.

## Architecture and Storage Backend

### SQLite Database Implementation

The cache controller persists all data to an SQLite file named `cache.db` located in the user's cache directory. During initialization, the controller calls `__create_database_tables` and `__create_database_indexes` to establish the required schema and optimize query performance.

Schema versioning is handled through the `_setup_database` method, which reads the stored version from `DB_TABLE_SETTINGS` and compares it against `DB_SCHEMA_VERSION`. When versions differ, `__migrate_database` executes the necessary `ALTER` statements—for example, adding the `allow_expired_cache` column to existing tables.

## Core API Methods

### Retrieving Cached Data

The `get` method in [`music_assistant/controllers/cache/controller.py`](https://github.com/music-assistant/server/blob/main/music_assistant/controllers/cache/controller.py) retrieves values by key while handling JSON deserialization, optional checksum validation, and expiration checks. If the cached entry is expired and `allow_expired_cache` is disabled, the method returns the default value.

```python
top_tracks = await mass.cache.get(
    key="artist_top_tracks",
    provider="spotify",
    category=1,
    default=[],
)

```

### Storing Data with Metadata

The `set` method handles JSON serialization and accepts parameters for **time-to-live (TTL)**, provider identification, category classification, checksums, and persistence flags. The `expiration` parameter defines the TTL in seconds, while `persistent` entries survive cleanup operations.

```python
await mass.cache.set(
    key="artist_top_tracks",
    data=["track1", "track2", "track3"],
    expiration=3600,  # 1 hour TTL

    provider="spotify",
    category=1,
)

```

### Cache Invalidation

The controller provides two removal mechanisms: `delete` for specific keys and `clear` for bulk operations. The `clear` method supports filtering by provider and includes a boolean `include_persistent` flag to protect persistent entries during cleanup.

```python

# Clear only non-persistent entries for a specific provider

await mass.cache.clear(
    provider_filter="spotify",
    include_persistent=False,
)

```

## Advanced Features

### Checksum-Based Validation

To prevent stale data usage, the controller supports optional checksums. When setting data, you provide a checksum string; when retrieving, you specify the expected checksum. If the stored checksum differs from the provided value, the `get` method returns the default.

```python
checksum = "v5"
await mass.cache.set(
    key="playlist_42",
    data=playlist_dict,
    checksum=checksum,
)

# Returns None if checksum doesn't match

cached = await mass.cache.get(
    key="playlist_42",
    checksum="v5",
    default=None,
)

```

### Cache Bypass Context Manager

The `handle_refresh` context manager temporarily toggles the `BYPASS_CACHE` thread-local flag defined in [`music_assistant/controllers/cache/constants.py`](https://github.com/music-assistant/server/blob/main/music_assistant/controllers/cache/constants.py). Within this context, all `get` operations return the default value, forcing fresh data fetches while allowing `set` operations to populate the cache with updated values.

```python
async with mass.cache.handle_refresh(bypass=True):
    fresh_data = await fetch_latest_data()
    await mass.cache.set(key="latest", data=fresh_data)

```

## Automatic Maintenance and Cleanup

### Background Cleanup Task

The `_register_cleanup_task` method schedules a daily background task that executes `auto_cleanup`. This routine deletes rows where the `expires` timestamp has passed, unless the entry is marked with `allow_expired_cache`. The controller also monitors the database file size against `MAX_CACHE_DB_SIZE_MB` and issues warnings when limits are exceeded.

### Configuration Interface

The controller exposes a configuration entry `CONF_CLEAR_CACHE` that appears in the Music Assistant UI. When invoked through the interface, this triggers the `clear()` method to wipe the cache.

## Key Files and Module Structure

The cache subsystem spans four primary files:

- **[`music_assistant/controllers/cache/controller.py`](https://github.com/music-assistant/server/blob/main/music_assistant/controllers/cache/controller.py)** – Full implementation of the Cache Controller including API methods, database handling, and cleanup logic.
- **[`music_assistant/controllers/cache/constants.py`](https://github.com/music-assistant/server/blob/main/music_assistant/controllers/cache/constants.py)** – Defines `MAX_CACHE_DB_SIZE_MB`, `DEFAULT_CACHE_EXPIRATION`, and the `BYPASS_CACHE` context variable.
- **[`music_assistant/controllers/cache/helpers.py`](https://github.com/music-assistant/server/blob/main/music_assistant/controllers/cache/helpers.py)** – Utility functions for JSON serialization and checksum handling.
- **[`music_assistant/controllers/cache/README.md`](https://github.com/music-assistant/server/blob/main/music_assistant/controllers/cache/README.md)** – High-level developer documentation for the caching layer.

## Summary

- The cache controller uses SQLite (`cache.db`) for persistent JSON storage, with automatic table creation and index optimization via `__create_database_tables` and `__create_database_indexes`.
- Public async methods `get`, `set`, `delete`, and `clear` provide full CRUD operations with support for TTL, checksums, and provider-based filtering.
- Schema migrations are handled automatically through `__migrate_database` when `DB_SCHEMA_VERSION` differs from the stored version.
- The `handle_refresh` context manager enables temporary cache bypassing using the `BYPASS_CACHE` thread-local flag.
- Automatic maintenance runs daily via `auto_cleanup`, removing expired entries while respecting `allow_expired_cache` flags and monitoring database size limits.

## Frequently Asked Questions

### Where is the cache data physically stored?

The cache controller stores all data in an SQLite file named `cache.db` located in the user's cache directory. This file is created automatically when the controller initializes, and its schema is managed through the `_setup_database` and `__migrate_database` methods in [`music_assistant/controllers/cache/controller.py`](https://github.com/music-assistant/server/blob/main/music_assistant/controllers/cache/controller.py).

### How does the cache handle schema updates?

When the controller initializes, it reads the stored schema version from `DB_TABLE_SETTINGS` and compares it to `DB_SCHEMA_VERSION` defined in the constants. If versions differ, `__migrate_database` executes the required `ALTER` statements to update the database structure without losing existing cached data.

### Can I force a cache refresh while bypassing stored values?

Yes. Use the `handle_refresh` context manager, which sets the `BYPASS_CACHE` thread-local flag. While inside this context, `get` operations will return default values, allowing you to fetch fresh data and repopulate the cache. This is particularly useful for "refresh-only" UI actions.

### What triggers automatic cache cleanup?

A daily background task registered via `_register_cleanup_task` executes `auto_cleanup`, which deletes rows where the `expires` timestamp has passed. Entries marked with `allow_expired_cache` are preserved. The controller also checks the database file size against `MAX_CACHE_DB_SIZE_MB` and logs warnings if the limit is exceeded.