Skip to content

improvement: Add an option to cache turbine results - #8772

Merged
tgodzik merged 3 commits into
scalameta:main-v2from
tgodzik:cache-turbine-results
Aug 10, 2026
Merged

improvement: Add an option to cache turbine results#8772
tgodzik merged 3 commits into
scalameta:main-v2from
tgodzik:cache-turbine-results

Conversation

@tgodzik

@tgodzik tgodzik commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added an optional Java Turbine compilation cache, disabled by default.
    • Enabled caching can reuse compiled symbols across workspace server restarts.
    • Added the java-turbine-cache configuration setting.
    • Modified Java sources now take precedence over cached compilation results.
  • Bug Fixes
    • Invalid, outdated, or unavailable cache data safely falls back to recompilation.
    • Cache read, write, and deletion failures no longer interrupt compilation.
  • Tests
    • Added coverage for cache persistence, symbol resolution, diagnostics, and changed source files.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7474ba2c-20a6-4267-b712-295bdd7498f1

📥 Commits

Reviewing files that changed from the base of the PR and between ca3586e and b7e1914.

📒 Files selected for processing (1)
  • metals/src/main/scala/scala/meta/internal/metals/mbt/MbtWorkspaceSymbolProvider.scala
🚧 Files skipped from review as they are similar to previous changes (1)
  • metals/src/main/scala/scala/meta/internal/metals/mbt/MbtWorkspaceSymbolProvider.scala

📝 Walkthrough

Walkthrough

Adds configurable Java Turbine caching. The cache stores lowered compilation results in a deterministic JAR, restores them on first compilation, and invalidates stale entries. MBT wiring and integration tests cover restart persistence and dirty Java source precedence.

Changes

Java Turine compilation cache

Layer / File(s) Summary
Cache configuration and workspace path
metals/src/main/scala/scala/meta/internal/metals/Configs.scala, metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala, metals/src/main/scala/scala/meta/internal/metals/Directories.scala, metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala
Adds the TurbineCacheConfig model, java-turbine-cache parsing and serialization, the .metals/turbine-cache.jar path, and configuration wiring.
Turbine cache persistence
metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCache.scala, metals/src/main/scala/scala/meta/internal/metals/mbt/GitVCS.scala
Writes and reads lowered class bytes in deterministic JAR files. Git HEAD validates cache entries. Invalid caches and non-fatal failures are handled.
Compiler and workspace integration
metals/src/main/scala/scala/meta/internal/metals/mbt/MbtWorkspaceSymbolProvider.scala, metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCompiler.scala
Creates a workspace-scoped cache, restores it on first compilation, adds dirty Java files to the source path, and writes new results.
Cache integration validation
tests/unit/src/test/scala/tests/mbt/TurbineCacheSuite.scala, tests/unit/src/test/scala/tests/UserConfigurationSuite.scala
Tests symbol resolution, cache creation, restart persistence, dirty-source precedence, and serialized configuration output.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LSP
  participant MbtWorkspaceSymbolProvider
  participant TurbineCompiler
  participant TurbineCache
  participant GitVCS
  LSP->>MbtWorkspaceSymbolProvider: initialize workspace
  MbtWorkspaceSymbolProvider->>TurbineCompiler: configure cache and dirty Java sources
  TurbineCompiler->>TurbineCache: loadFromCache(classpath)
  TurbineCache->>GitVCS: getHeadHash(workspace)
  GitVCS-->>TurbineCache: Git HEAD key
  alt valid cache
    TurbineCache-->>TurbineCompiler: cached compilation result
    TurbineCompiler->>TurbineCompiler: add dirty Java files to sourcepath
  else no valid cache
    TurbineCompiler->>TurbineCompiler: compile sources
    TurbineCompiler->>TurbineCache: writeCache(result)
  end
Loading

Possibly related PRs

Suggested reviewers: zielinsky

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the pull request's main change: adding an option to cache Turbine results.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@tgodzik
tgodzik force-pushed the cache-turbine-results branch from d328154 to c159536 Compare August 5, 2026 13:32
This priduces a jar in .metals directory that is later read by metals into turbine.
@tgodzik
tgodzik force-pushed the cache-turbine-results branch from c159536 to 5e0e70d Compare August 6, 2026 12:10
@tgodzik
tgodzik marked this pull request as ready for review August 6, 2026 12:11
@tgodzik
tgodzik requested a review from zielinsky August 6, 2026 12:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCache.scala`:
- Around line 101-138: Update
metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCache.scala lines
101-138 and the read/write cache flow around readCache to persist a versioned
cache key covering the source revision and effective JAR classpath, validate it
before returning TurbineCompileResult, and delete mismatched entries. Update
metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCompiler.scala lines
196-235 to compute and provide the expected key when loading the cache, falling
back to compilation when validation fails.

In `@metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala`:
- Line 110: Update UserConfiguration.toString’s fields list to include
javaTurbineCache.enabled alongside javaTurbineRecompileDelay, so serialized
configuration output exposes the cache setting.

In `@tests/unit/src/test/scala/tests/mbt/TurbineCacheSuite.scala`:
- Around line 189-195: Extend the restart scenario around assertHovers and
doCompileNow with a test-only observable that records Turbine cache loads, then
assert the observable confirms the restarted TurbineCompiler loaded the
unchanged cache rather than recompiling. Keep the existing hover and cache-file
assertions, and ensure the observable is reset or scoped so it specifically
verifies cache reuse after newServer().
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 59133c64-92f6-47eb-a441-3cb1483987e1

📥 Commits

Reviewing files that changed from the base of the PR and between 2939548 and 5e0e70d.

📒 Files selected for processing (8)
  • metals/src/main/scala/scala/meta/internal/metals/Configs.scala
  • metals/src/main/scala/scala/meta/internal/metals/Directories.scala
  • metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala
  • metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala
  • metals/src/main/scala/scala/meta/internal/metals/mbt/MbtWorkspaceSymbolProvider.scala
  • metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCache.scala
  • metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCompiler.scala
  • tests/unit/src/test/scala/tests/mbt/TurbineCacheSuite.scala

Comment thread metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCache.scala Outdated
Comment thread tests/unit/src/test/scala/tests/mbt/TurbineCacheSuite.scala

@odisseus odisseus left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks good. But I wonder how it is affected by unstaged files or Git submodules: AFAIK neither of these affect the head hash.

@tgodzik

tgodzik commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

The code looks good. But I wonder how it is affected by unstaged files or Git submodules: AFAIK neither of these affect the head hash.

We now add all dirty files to sourcepath, so any changes from the last hash should be picked up.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCompiler.scala (1)

230-235: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not restore dirty compilation output by Git HEAD alone.

A compilation with dirty Java sources writes cached classfiles under the unchanged HEAD hash. If the user then reverts those sources and restarts Metals, the worktree is clean, the HEAD hash still matches, and Line 230 restores the stale dirty classfiles without dirty sources to shadow them.

Skip cache writes when relevant compilation inputs are dirty, while retaining the last clean cache entry. Alternatively, include a deterministic worktree fingerprint in the cache key. Add a regression test that compiles dirty Java, reverts it, restarts, and verifies that removed dirty symbols do not resolve.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCompiler.scala`
around lines 230 - 235, The Turbine cache key currently permits dirty Java
compilation output to be restored after the worktree returns to HEAD. Update the
cache read/write flow around isFirstCompile, loadFromCache, and
TurbineCompiler.validClasspaths so dirty compilation inputs do not overwrite or
restore the last clean cache entry; use a deterministic worktree fingerprint if
that is the established approach. Add a regression test covering dirty Java
compilation, reverting the changes, restarting, and confirming removed dirty
symbols no longer resolve.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@metals/src/main/scala/scala/meta/internal/metals/mbt/MbtWorkspaceSymbolProvider.scala`:
- Around line 160-170: Update the dirty compilation-unit flow around the
`documents.get(status.file)` lookup in the workspace symbol provider to derive
an `IndexedDocument` from the current source input, so untracked Java files and
package moves use current metadata instead of stale or missing indexed
documents. Preserve the existing Git status filtering and compilation-unit
generation, and add restart coverage for both an untracked Java file and a Java
package relocation.

---

Outside diff comments:
In `@metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCompiler.scala`:
- Around line 230-235: The Turbine cache key currently permits dirty Java
compilation output to be restored after the worktree returns to HEAD. Update the
cache read/write flow around isFirstCompile, loadFromCache, and
TurbineCompiler.validClasspaths so dirty compilation inputs do not overwrite or
restore the last clean cache entry; use a deterministic worktree fingerprint if
that is the established approach. Add a regression test covering dirty Java
compilation, reverting the changes, restarting, and confirming removed dirty
symbols no longer resolve.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f5307473-32b1-48e6-aab7-16bf2c79fdfd

📥 Commits

Reviewing files that changed from the base of the PR and between 5e0e70d and ca3586e.

📒 Files selected for processing (7)
  • metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala
  • metals/src/main/scala/scala/meta/internal/metals/mbt/GitVCS.scala
  • metals/src/main/scala/scala/meta/internal/metals/mbt/MbtWorkspaceSymbolProvider.scala
  • metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCache.scala
  • metals/src/main/scala/scala/meta/internal/metals/mbt/TurbineCompiler.scala
  • tests/unit/src/test/scala/tests/UserConfigurationSuite.scala
  • tests/unit/src/test/scala/tests/mbt/TurbineCacheSuite.scala

@tgodzik
tgodzik force-pushed the cache-turbine-results branch from ca3586e to b7e1914 Compare August 7, 2026 16:31
@tgodzik

tgodzik commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Will go ahead and merge it for people to test, since it's under the flag.

@tgodzik
tgodzik merged commit 6e02127 into scalameta:main-v2 Aug 10, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants