Skip to content

An item reached by several forEnv() calls at once is resolved once per caller - #1288

Merged
theoephraim merged 2 commits into
dmno-dev:mainfrom
voiys:fix-concurrent-item-resolve
Nov 13, 2026
Merged

theoephraim merged 2 commits into
dmno-dev:mainfrom
voiys:fix-concurrent-item-resolve

Conversation

@voiys

@voiys voiys commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor
# @currentEnv=$APP_ENV
# ---
# @type=enum(dev, prod)
APP_ENV=exec(`sh -c 'echo run >> count.txt; echo dev'`)
# @required=forEnv(prod)
API_URL=
# @required=forEnv(prod)
DB_URL=
# @required=forEnv(prod)
REDIS_URL=
# @reqired                      <- any schema error
PORT=3000

graph.finishLoad(); graph.resolveEnvValues()

main:  count.txt has 4 lines
here:  count.txt has 1 line

cause: with a schema error, finishLoad doesn't resolve the root decorators, so APP_ENV is first resolved through forEnv(). every forEnv item runs in parallel, each calls earlyResolve on APP_ENV, and each gets past the isResolved check before any of them has finished. each one runs the resolver and appends its own validation warnings.

fix: concurrent ConfigItem.resolve() calls share one in-flight promise.

a call from inside the item's own resolution is the exception and goes straight through, as it did before. otherwise this hangs, which the first version of this PR did (caught in review):

# @currentEnv=$APP_ENV
# ---
# @type=enum(dev, prod) @required=forEnv(prod)     <- asks for the env while APP_ENV is resolving
APP_ENV=dev

context: found while prototyping a language server (#469), which resolves a schema while it still has errors. the CLI stops on schema errors before it resolves anything, so no CLI user sees this.

test: concurrent-resolve.test.ts. the two sharing cases fail on main, and the two self-reference cases guard the hang above.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

Promise sharing introduces a self-wait deadlock during current-env resolution. This needs addressing before merging.

Reviewed changes: Reviewed the concurrent item-resolution fix and its regression coverage.

  • Shared resolution: Concurrent non-reset ConfigItem.resolve() callers share one pending run, with cleanup after settlement.
  • Regression coverage: Two tests cover duplicate execution and warnings from parallel forEnv() calls, accompanied by a varlock patch entry.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using gpt-6.1-sol | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes: Reviewed the reentrant-resolution fix since the previous review.

  • Preserved reentrant progress: Added async call-chain tracking so an item's own nested resolution bypasses promise sharing without blocking independent callers.
  • Added deadlock coverage: Tested forEnv() on both the current-env item and its value dependency. All 1,204 env-graph tests passed.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

…) calls

With a schema error anywhere, finishLoad does not resolve the root
decorators, so the current-env item is first resolved through forEnv().
Every forEnv item resolving in parallel called earlyResolve on it, each
passed the isResolved check before any had finished, and each ran the
item's resolver and appended its own validation warnings.
Sharing the in-flight promise also caught reentrant calls: with
@required=forEnv(prod) on the @currentEnv item (or on an item it is built
from), the item's metadata asks for the current env, which resolves that
same item and then waited on its own run forever.

A call made from inside the item's own run now runs the resolution body
directly, as before this change. Calls from other items still share one
run.
@theoephraim
theoephraim force-pushed the fix-concurrent-item-resolve branch from d0450bf to 8f93283 Compare November 13, 2026 00:47
@theoephraim
theoephraim merged commit 9fce25d into dmno-dev:main Nov 13, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants