Skip to content

Use canonical source file type in the RPC edit-batch gate#7929

Merged
knutwannheden merged 1 commit into
mainfrom
rpc-batch-canonical-source-file-type
Jun 7, 2026
Merged

Use canonical source file type in the RPC edit-batch gate#7929
knutwannheden merged 1 commit into
mainfrom
rpc-batch-canonical-source-file-type

Conversation

@knutwannheden

@knutwannheden knutwannheden commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

What's changed

RecipeRunCycle's edit-phase BatchVisit optimization keyed off the raw src.getClass().getName() in two places:

  1. the language gate that decides whether a source file can be batched — currentRpc.getLanguages().contains(src.getClass().getName()); and
  2. flushBatch's rpc.getObject(id, originalBefore.getClass().getName()) that fetches the batched result.

Both now use DynamicDispatchRpcCodec.canonicalSourceFileType(...) instead.

Why

getLanguages() advertises canonical, codec-registered type names (e.g. org.openrewrite.xml.tree.Xml$Document), and every other RPC sourceFileType is derived via DynamicDispatchRpcCodec.canonicalSourceFileType(...).

  • the batch language gate failed for every such source, and
  • because a recipe that is "in a batch" but fails the gate returns without falling through to the non-batch path, every batched recipe but the last was silently skipped (the trailing recipe has no next recipe, so it isn't batched and runs via the non-batch path).

The net effect: a multi-recipe composite of same-RPC recipes (e.g. a language migration) applied only its last sub-recipe when run against a lazily-loaded LST. Recipes ran correctly standalone and in-process (InMemoryLargeSourceSet, where sources are the real type), which masked the issue.

DynamicDispatchRpcCodec.canonicalSourceFileType already walks a subclass up to its registered supertype (see SubclassCodecDispatchTest), so using it here makes the gate and the result fetch agree with the rest of the RPC pipeline.

Test plan

  • New CompositeBatchVisitSubclassTest (rewrite-xml) runs a two-recipe composite (ChangeTagValue + ChangeTagAttribute) over an RPC peer against a LazyXmlDocument (a subclass of Xml.Document, mirroring a lazy LST proxy), and asserts both recipes applied.
  • Verified the test fails without this change (only the trailing recipe runs — the child tag keeps its old value) and passes with it.
  • ./gradlew :rewrite-xml:test --tests "org.openrewrite.xml.rpc.CompositeBatchVisitSubclassTest" green.

The edit-phase BatchVisit optimization in RecipeRunCycle keyed off the raw
src.getClass().getName() in two places: the language gate that decides whether
a source can be batched, and flushBatch's getObject call that fetches the
batched result. getLanguages() (and every other RPC sourceFileType) uses the
canonical, codec-registered type name from DynamicDispatchRpcCodec.

Lazily-loaded LSTs (e.g. the Moderne CLI's V3 format) hand the scheduler
generated subclasses of the real tree type, whose getClass().getName() is the
subclass name. The raw-name comparison therefore never matched for those, so
the batch language gate failed and every batched recipe but the last was
silently skipped (the trailing recipe falls through to the non-batch path).

Compare and fetch by canonical source file type instead.
@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Jun 7, 2026
@knutwannheden knutwannheden merged commit 339008a into main Jun 7, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Jun 7, 2026
@knutwannheden knutwannheden deleted the rpc-batch-canonical-source-file-type branch June 7, 2026 05:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant