Caching

View as Markdown

Blockstudio includes persistent file-backed caches. They are enabled by default through the cache.enabled setting and are written to:

Code
wp-content/blockstudio/cache/

This one configured root owns runtime build payloads, prebuilt block registration data, editor assets, Tailwind CSS, rendered documents, island fragments, static HTML, graph indexes, warm queues, and cache diagnostics. This avoids repeating discovery, parsing, compilation, and rendering work while keeping all generated runtime state under one host-manageable boundary.

The default deliberately sits outside uploads, where hardened hosts may block PHP cache payloads. Change the location with cache.path; relative paths resolve from WP_CONTENT_DIR, while absolute paths support dedicated writable cache volumes:

JSON
{
  "cache": {
    "enabled": true,
    "path": "cache/blockstudio"
  }
}

For environment-specific configuration, use either the setting filter or the resolved-directory filter:

PHP
add_filter('blockstudio/settings/cache/path', function () {
    return '/srv/wordpress-cache/blockstudio';
});

add_filter('blockstudio/cache/dir', function (string $directory): string {
    return WP_CONTENT_DIR . '/cache/blockstudio';
});

Changing the path starts with a cold cache. Old files under wp-content/uploads/blockstudio/cache are no longer read and can be removed.

Identity and invalidation

Every scope is nested under a network/blog identity and a complete runtime identity. Cache entries change when their inputs change, including watched block files, field files, asset dependencies, settings, the active theme, active plugins, logical discovery inventories, WordPress, PHP, and Blockstudio versions. Item-specific dependency hashes isolate changed-only builds from unrelated objects.

Logical discovery sources also contribute their source ID, content fingerprint, and watch inputs. Alternate runtime selections can add a blockstudio/cache/context value to isolate all cache scopes. Hosts whose tenant boundary differs from WordPress network/blog IDs can filter blockstudio/cache/site_key; the result is sanitized before it becomes a path segment.

On a warm frontend request, Blockstudio hydrates the runtime registry before it constructs discovery sources. A valid cache hit therefore skips recursive source discovery, block construction, field parsing, and repeated cache-key tree walks.

In production and staging, a validated watch snapshot is trusted for 20 seconds before Blockstudio stats its files and directories again. local and development environments default to zero so source edits are visible immediately. Change that interval with:

PHP
add_filter('blockstudio/cache/watch_debounce', function () {
    return 30;
});

The debounce can delay automatic source-change detection by at most the configured number of seconds. Required compiled assets are still checked on every cache hit, so deleting an _dist directory invalidates the runtime payload immediately instead of serving raw CSS or dropping SCSS until the window expires. Set the debounce to 0 when a production-like environment is also used for active file authoring.

Concurrency and retention

Cold cache keys use a single-flight lock. One request builds the payload while concurrent requests wait for the atomically published result instead of repeating the same discovery or compilation work. Atomic publication prevents partial objects. When a refresh fails, a valid stale last-good object remains available, the failure is observable, and the next request can recover.

Runtime scopes retain 20 published payloads by default. Adjust the cap per scope when an integration intentionally creates more cache contexts:

PHP
add_filter(
    'blockstudio/cache/max_files_per_scope',
    function (int $maximum, string $scope): int {
        return 'runtime' === $scope ? 50 : $maximum;
    },
    10,
    2
);

Retention never evicts a file the installed early-serve map references, and the blockstudio/cache/protected_paths filter marks additional paths that pruning must leave alone.

Unused namespaces are collected automatically instead of accumulating on every plugin activation and PHP or WordPress update. A deployment snippet that used to glob the old Tailwind cache directory should call Blockstudio\Runtime_Cache::purge('tailwind') instead.

When an upgraded installation contains the pre-7.6 flat cache/runtime directory, Blockstudio atomically renames it out of the active path. A WP-Cron job then removes at most 500 filesystem entries per pass. This keeps old payload and .build.lock trees from consuming disk without making the request that detects a large directory recursively delete it. The cleanup runs only while cache.enabled is active; change its batch size with blockstudio/cache/legacy_cleanup_batch_size when a host needs smaller or larger passes.

Disable the persistent caches in blockstudio.json:

JSON
{
  "cache": {
    "enabled": false
  }
}

Or via filter:

PHP
add_filter('blockstudio/settings/cache/enabled', '__return_false');

The in-memory per-request render cache described under Performance is intentionally separate, but its persistent artifacts use this same filesystem boundary.

Tailwind cache

Tailwind’s compiled CSS cache uses the shared runtime root under its network/site/runtime namespace and tailwind scope. It stores up to 1,000 entries for 30 days by default, enough to retain page-level keys for a complete site instead of evicting them after a small number of routes.

Tailwind also reuses a warm compiler for the same CSS input inside long-lived processes and persists compiler construction state between processes. Cold page candidate sets still compile independently, but they do not need to rebuild the framework parser each time.

Use these filters to change retention:

PHP
add_filter('blockstudio/tailwind/cache_max_files', function () {
    return 1500;
});

add_filter('blockstudio/tailwind/cache_max_age', function () {
    return 14 * DAY_IN_SECONDS;
});

The minimum age is one hour.

Static Prerendering

The anonymous HTML cache that lives under this same root.