Skip to content

loader: skip re-merge of identical resources imported through diamond includes - #906

Merged
ndeloof merged 1 commit into
compose-spec:mainfrom
davireis:fix-diamond-include-duplication
Aug 12, 2026
Merged

ndeloof merged 1 commit into
compose-spec:mainfrom
davireis:fix-diamond-include-duplication

Conversation

@davireis

@davireis davireis commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

What I did

Restored the identical-resource fast path in importResource that #835 removed: when a resource re-imported through another include path is deeply equal to the already-imported definition, skip the merge. Genuinely different definitions keep the #835 override-merge behavior. Added a regression test with a diamond include fixture.

Why

Since #835, a file reached through multiple include: paths (a diamond) is re-merged once per path, and list-valued extension fields get their entries appended per re-merge — a service included through N paths ends with N copies of every x-* list entry:

# base.yaml
services:
  shared:
    image: busybox
    build:
      context: .
      x-bake:
        cache-from:
          - type=registry,ref=example/cache:a
# left.yaml / right.yaml:   include: [./base.yaml]
# compose.yaml:             include: [./left.yaml, ./right.yaml]

docker compose config | grep -c 'example/cache:a' → 1 on CLI v2.40.3 (compose-go v2.9.1), 2 on v5.0.0+/v2.10.0+; each additional include path adds another copy. Bisected to 3d3411d (#835). Spec-schema fields are unaffected (normalized downstream), which is why this went unnoticed.

Real-world impact: a monorepo integration-test tree with ~60 include paths to shared services saw its resolved config output grow from 2,043 to 127,725 cache entries (1.1 MB → 21 MB), and resolved output became dependent on which Docker Desktop version a developer runs.

Testing

  • New TestLoadWithDiamondInclude fails on master (2 != 1) and passes with the fix.
  • go test ./loader/ ./override/ pass.

… includes

Since 3d3411d (compose-spec#835), importResource merges a re-imported resource into
the already-imported definition unconditionally. When the same file is
reached through multiple include paths, the identical definition is
re-merged once per path, and list-valued extension fields (e.g. x-bake
under build) get their entries appended per path: a service included
through N paths ends with N copies of every extension list entry.

Restore the pre-compose-spec#835 fast path: when the re-imported definition is
deeply equal to the existing one, skip the merge. Genuinely different
definitions keep the compose-spec#835 override-merge behavior.

Signed-off-by: Davi De Castro Reis <davi@davi.eng.br>
@davireis
davireis requested a review from ndeloof as a code owner August 7, 2026 17:28
@ndeloof
ndeloof enabled auto-merge (rebase) August 11, 2026 15:04
@ndeloof
ndeloof disabled auto-merge August 12, 2026 09:47
@ndeloof
ndeloof merged commit 5ebe0d7 into compose-spec:main Aug 12, 2026
8 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