Skip to content

CD: resolve same-app includes in-process, with per-type transport (IN_PROCESS | HTTP) #220

Closed

Description

@MoonWorm

Problem

The CD plugin fetches every included type over HTTP, including the types the app serves itself, by calling itself on loopback. Each same-app include:

  • pays a full round trip: a socket, every servlet filter, and the JSON serialised and parsed again;
  • holds a second worker thread while the original request waits, so a bounded pool can starve under load;
  • breaks wherever loopback can't reach the app: TLS terminated in the app, a server bound to one address, mTLS, graceful shutdown, MockMvc.

Solution

Resolve same-app types in-process, through JsonApi4j.execute(...), as the client's principal. Validation and the AC plugin still apply.

Types mapped to another service's url stay over HTTP. HTTP for same-app types becomes an explicit, per-type choice:

jsonapi4j:
  cd:
    mapping:
      default:
        url: http://127.0.0.1:8080/jsonapi   # this app's own address
        transport: HTTP                      # same-app types over HTTP by default
      users:
        transport: IN_PROCESS                # except users
      orders:
        url: https://orders.internal/jsonapi # another service: always HTTP
  • transport: IN_PROCESS | HTTP per mapping entry. An unset transport falls back to mapping.default.transport, then to IN_PROCESS.
  • Startup validation:
    • HTTP without mapping.default.url is rejected;
    • IN_PROCESS on a type with a url is rejected;
    • default takes only url and transport.
  • No more loopback-URL detection.
  • In-process results bypass the include cache: they're cheap to read and always fresh.

A rough benchmark on a 3-hop include: in-process is ~1.8× faster per request, at ~1.6× the throughput.

Refactoring (resolver)

  • DomainSettings is a sealed interface with two kinds: overHttp(...) and inProcess(...).

  • Fetching is split into composable BatchFetcher<R> decorators:

    OverHttp  → Caching → Chunking → ErrorStrategyAware → HTTP client
    InProcess →           Chunking → ErrorStrategyAware → InProcessBatchFetcher
    

    RoutingBatchFetcher dispatches by the route kind and stands in a null object when there is no in-process fetcher.

  • HttpFetchResult is renamed to FetchResult. CachingCompoundDocsFetcher, BatchFetchResult and PrimaryResource are removed. Resource parsing is unified in JsonApiResponseParser.

  • CompoundDocsRequest carries the principal. ResponseHeaders and ResponseStatus gain clear(), and ResponseHeaders also gains takeCacheControl().

Breaking changes

  • Same-app includes no longer pass through servlet filters or URL-based security rules. To keep that, set transport: HTTP.
  • mapping.default.url alone no longer switches to HTTP; it now also needs mapping.default.transport: HTTP.
  • SelfFallbackRouting.forRequest is removed.
  • The resolver API renames listed above (HttpFetchResult → FetchResult, and the removed classes).

Docs

  • compound-docs-plugin.md: how includes are fetched, the transport settings, and the Limitations section.
  • performance.md: prefer IN_PROCESS.
  • compound-docs.md.
  • The Claude Code plugin references.

Activity

  1. added a commit that references this issue on Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions