Skip to content

Conversation

@michaelquery
Copy link

@michaelquery michaelquery commented Feb 10, 2026

Fixes #10383

Summary

Template caches (compiledYamlCache, compiledJinjaCache, compiledScriptCache) used only MD5(file.content) as the cache key. When schemaVersion changed and recompilation was triggered, templates were served from cache because file content hadn't changed.

This broke the documented behavior of schema_version, which states:

schema_version can be used to tell Cube that the data model should be recompiled in case it depends on dynamic definitions fetched from some external database or API.

If the returned string is different, the data model will be recompiled.

The Fix

Include compilerId (already available as an instance property) in the cache key hash:

// Before
const cacheKey = crypto.createHash('md5').update(JSON.stringify(file.content)).digest('hex');

// After  
const cacheKey = crypto.createHash('md5').update(JSON.stringify(file.content)).update(this.compilerId).digest('hex');

When schemaVersion changes → recompilation triggered → new compilerId → cache miss → templates re-rendered.

Minimal Reproduction (before fix)

# model/cubes/test_recompile.yml.jinja
cubes:
  - name: test_recompile
    sql: "SELECT 1 as id, '{{ get_compile_timestamp() }}' as compiled_at"
    dimensions:
      - name: id
        sql: id
        type: number
        primary_key: true
      - name: compiled_at
        sql: compiled_at
        type: string
# cube.py
from cube import config
from datetime import datetime
import random

@config("schema_version")
def schema_version(ctx: dict) -> str:
    return str(random.random())  # Every request triggers recompilation

def get_compile_timestamp() -> str:
    return datetime.now().isoformat()

Expected: compiled_at shows new timestamp on every request (since schema_version changes)

Actual (before fix): Timestamp never changes—template served from cache despite "Recompiling schema" log

Current Workaround (and why it's problematic)

The only workaround is to include schema-affecting config in app_id:

@config("context_to_app_id")  
def context_to_app_id(ctx: dict) -> str:
    return f"APP_{tenant_id}_{config_version}"  # Forces new CompilerApi

This causes memory bloat and resource duplication because each app_id creates a new CompilerApi with its own resources, including data model compile cache, SQL compile cache, query queues, in-memory result caching, etc. The multitenancy docs explicitly warn about this overhead:

"Each unique id generated by contextToAppId or contextToOrchestratorId will generate a dedicated set of resources, including data model compile cache, SQL compile cache, query queues, in-memory result caching, etc."

"those resources can have a pretty sizable memory footprint ranging from single-digit MBs on the lower end and dozens of MBs on the higher end"

Side Effects

Scenario Before After
schema_version changes Cache hit (stale) ❌ Cache miss ✅
Dev mode: any file changes Changed file misses; unchanged files hit All files miss
Production (no schema_version) No change No change

In dev mode, when any file changes, a new compilerId is generated. This means all templates are re-rendered, not just the changed file. Previously, unchanged templates would hit the cache.

Why this is acceptable:

  • Dev mode is for rapid iteration—template rendering is fast
  • The previous behavior (caching unchanged templates across recompilations in dev mode) was arguably inconsistent with the "recompiling schema" semantics
  • Production deployments without devServer are unaffected

If a more surgical fix is preferred, compilerId could be conditionally included only for templates containing Jinja syntax (indicating dynamic content).

Test Plan

  • Added unit tests verifying cache behavior with different compilerId values
  • Tests pass: yarn test --testPathPattern="template-cache-invalidation"
  • Linting passes

Template caches (YAML, Jinja, JS) used only MD5(file.content) as the
cache key. When schemaVersion changed and recompilation was triggered,
templates were served from cache because file content hadn't changed.

This broke the documented behavior of schema_version, which states:
"schema_version can be used to tell Cube that the data model should
be recompiled in case it depends on dynamic definitions fetched from
some external database or API."

By including compilerId in the cache key, templates are now properly
re-rendered when a new compilation is triggered via schemaVersion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions github-actions bot added javascript Pull requests that update Javascript code pr:community Contribution from Cube.js community members. labels Feb 10, 2026
@michaelquery michaelquery marked this pull request as ready for review February 10, 2026 03:27
@michaelquery michaelquery requested a review from a team as a code owner February 10, 2026 03:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

javascript Pull requests that update Javascript code pr:community Contribution from Cube.js community members.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Template caches ignore schemaVersion, breaking documented behavior for dynamic data models

1 participant