Skip to content

Conversation

@ardatan
Copy link
Owner

@ardatan ardatan commented Feb 26, 2025

No description provided.

@changeset-bot
Copy link

changeset-bot bot commented Feb 26, 2025

🦋 Changeset detected

Latest commit: 8efeb52

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 26 packages
Name Type
@graphql-tools/executor Patch
@graphql-tools/utils Patch
@graphql-tools/graphql-tag-pluck Patch
@graphql-tools/import Patch
@graphql-tools/links Patch
@graphql-tools/load Patch
@graphql-tools/merge Patch
@graphql-tools/mock Patch
@graphql-tools/node-require Patch
@graphql-tools/relay-operation-optimizer Patch
@graphql-tools/resolvers-composition Patch
@graphql-tools/schema Patch
@graphql-tools/apollo-engine-loader Patch
@graphql-tools/code-file-loader Patch
@graphql-tools/git-loader Patch
@graphql-tools/github-loader Patch
@graphql-tools/graphql-file-loader Patch
@graphql-tools/json-file-loader Patch
@graphql-tools/module-loader Patch
@graphql-tools/url-loader Patch
@graphql-tools/executor-apollo-link Patch
@graphql-tools/executor-envelop Patch
@graphql-tools/executor-legacy-ws Patch
@graphql-tools/executor-urql-exchange Patch
@graphql-tools/executor-yoga Patch
graphql-tools Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2025

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Resolved potential memory leaks by improving the cancellation of asynchronous operations.
  • Refactor

    • Streamlined the execution flow to manage abort signals more effectively, ensuring prompt cleanup of ongoing processes.
  • Tests

    • Updated asynchronous cancellation scenarios to enhance the overall reliability and responsiveness of the application.

Walkthrough

The changes refactor the abort signal management in the GraphQL execution process and its associated tests. Outdated functions such as registerAbortSignalListener and parts of getAbortPromise have been removed. New properties and mechanisms, including onSignalAbort and signalPromise in the execution context, are introduced. Additionally, asynchronous handling in test cases has been adjusted, and minor formatting improvements have been applied in a test file. A documentation file has also been added to describe these changes.

Changes

Files Change Summary
.changeset/swift-geese-behave.md Added documentation describing the removal of the registerAbortSignalListener method and detailing changes related to abort signal management in GraphQL execution.
packages/executor/src/execution/__tests__/abort-signal.test.ts,
packages/executor/src/execution/__tests__/stream-test.ts
Updated tests: changed promise handling in the subscription resolver from .then to .finally, removed async from a resolver, refactored mutation test structure, and simplified object formatting in the stream test.
packages/executor/src/execution/execute.ts,
packages/executor/src/execution/promiseForObject.ts,
packages/utils/src/registerAbortSignalListener.ts
Refactored abort signal handling by removing old functions (registerAbortSignalListener, parts of getAbortPromise), introducing a promise-based approach with createDeferredPromise, and adding new execution context properties (onSignalAbort, signalPromise) along with corresponding updates to function signatures.

Sequence Diagram(s)

sequenceDiagram
  participant C as Client
  participant E as Executor
  participant AC as AbortSignal
  participant EC as ExecutionContext
  C->>E: Send GraphQL query with AbortSignal
  E->>EC: Build Execution Context (with onSignalAbort & signalPromise)
  AC->>EC: Trigger abort signal event
  EC->>E: Propagate abort (reject signalPromise)
  E->>C: Return abort error (DOMException)
Loading

Poem

Hoppy rabbit here with glee,
Abort signals now flow so free,
Code refactored with care and art,
No more leaks to weigh the heart,
I nibble on changes big and small—
Leaping forward, proud of it all!
🐰🚀

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/executor/src/execution/__tests__/abort-signal.test.ts

Oops! Something went wrong! :(

ESLint: 9.22.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

packages/executor/src/execution/__tests__/stream-test.ts

Oops! Something went wrong! :(

ESLint: 9.22.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

packages/executor/src/execution/execute.ts

Oops! Something went wrong! :(

ESLint: 9.22.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

  • 2 others

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@theguild-bot
Copy link
Collaborator

theguild-bot commented Feb 26, 2025

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
@graphql-tools/executor 1.4.5-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/executor-apollo-link 1.0.18-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/executor-envelop 3.0.26-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/executor-legacy-ws 1.1.16-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/executor-urql-exchange 1.0.18-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/executor-yoga 3.0.26-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/graphql-tag-pluck 8.3.18-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
graphql-tools 9.0.17-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/import 7.0.17-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/links 9.0.26-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/load 8.0.18-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/apollo-engine-loader 8.0.19-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/code-file-loader 8.1.19-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/git-loader 8.0.23-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/github-loader 8.0.19-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/graphql-file-loader 8.0.18-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/json-file-loader 8.0.17-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/module-loader 8.0.17-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/url-loader 8.0.30-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/merge 9.0.23-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/mock 9.0.21-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/node-require 7.0.19-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/relay-operation-optimizer 7.0.18-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/resolvers-composition 7.0.17-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/schema 10.0.22-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎
@graphql-tools/utils 10.8.5-alpha-20250313112505-8efeb528d413c0973336fb16b420b5ff158d18f2 npm ↗︎ unpkg ↗︎

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2025

💻 Website Preview

The latest changes are available as preview in: https://fd7aeab3.graphql-tools.pages.dev

…handle listeners inside the execution context
@ardatan ardatan marked this pull request as ready for review March 13, 2025 11:23
@ardatan ardatan requested a review from enisdenjo March 13, 2025 11:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
packages/executor/src/execution/__tests__/stream-test.ts (1)

586-586: Simple style improvement.

This change condenses a multi-line object declaration into a single line, making the code more concise while maintaining readability for this simple object.

Consider applying this condensed style consistently to other similar objects in the file for better uniformity.

packages/utils/src/registerAbortSignalListener.ts (1)

36-39: Great optimization for already aborted signals.

Moving the abort check to the beginning of the function and immediately returning a rejected promise is a significant optimization. This prevents unnecessary Promise creation when the signal is already aborted.

Consider also making the inner check at line 41 an else if since it's now redundant with your early check.

if (signal.aborted) {
  return fakeRejectPromise(signal.reason);
}
return new Promise<void>((_resolve, reject) => {
-  if (signal.aborted) {
-    reject(signal.reason);
-    return;
-  }
  registerAbortSignalListener(signal, () => {
    reject(signal.reason);
  });
});
packages/executor/src/execution/__tests__/abort-signal.test.ts (1)

165-179: Simplify promise chaining in the test for better readability.

Wrapping normalizedExecutor with Promise.resolve().then(...) is functionally correct but can be simplified using a direct call in the expect block:

- await expect(
-   Promise.resolve().then(() =>
-     normalizedExecutor({...})
-   ),
- ).rejects.toBeInstanceOf(DOMException);

+ await expect(
+   normalizedExecutor({...})
+ ).rejects.toBeInstanceOf(DOMException);
packages/executor/src/execution/execute.ts (1)

128-129: Document usage of new onSignalAbort and signalPromise fields.

Adding doc comments clarifies how callers should implement custom abort handlers and handle the resulting promise rejections.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2869c66 and 8efeb52.

📒 Files selected for processing (6)
  • .changeset/swift-geese-behave.md (1 hunks)
  • packages/executor/src/execution/__tests__/abort-signal.test.ts (3 hunks)
  • packages/executor/src/execution/__tests__/stream-test.ts (1 hunks)
  • packages/executor/src/execution/execute.ts (12 hunks)
  • packages/executor/src/execution/promiseForObject.ts (3 hunks)
  • packages/utils/src/registerAbortSignalListener.ts (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: deployment
  • GitHub Check: Unit Test on Node 18 (windows-latest) and GraphQL v16
🔇 Additional comments (18)
.changeset/swift-geese-behave.md (1)

1-8: LGTM: Changeset provides clear description of the PR's purpose.

The changeset correctly documents the purpose of the PR: removing the leaking registerAbortSignalListener and handling listeners inside the execution context. This aligns with the PR objectives and provides adequate information for package updates.

packages/utils/src/registerAbortSignalListener.ts (1)

1-1: Import added for handling already aborted signals.

Adding the fakeRejectPromise import is necessary for the optimized abort signal handling implementation.

packages/executor/src/execution/promiseForObject.ts (4)

1-1: Simplified imports.

The import statement has been streamlined to only include what's needed from the promise helpers package, removing dependency on getAbortPromise.


14-18: Added signalPromise parameter for improved abort handling.

The new signalPromise parameter provides a more flexible way to handle abort signals without relying on the potentially leaking getAbortPromise function.


19-19: Early abort detection added.

This change ensures that if the signal is already aborted, an exception is thrown immediately rather than proceeding with potentially unnecessary work.


37-39: Improved abort signal handling.

Using Promise.race with the provided signalPromise is a cleaner approach than the previous implementation with getAbortPromise. This change aligns with the PR's goal of fixing memory leaks related to abort signal handling.

packages/executor/src/execution/__tests__/abort-signal.test.ts (2)

37-37: Use of finally correctly handles both success and rejection cases.

This ensures that stopped = true is set regardless of whether the promise resolves or rejects, improving reliability.


153-153: Removal of async is appropriate since no asynchronous tasks remain.

The function no longer requires the overhead of an async signature.

packages/executor/src/execution/execute.ts (10)

424-425: Immediate signal check ensures early exit if already aborted.

This is consistent with good abort handling practices.


495-495: Redundant safety check for the abort signal.

This second explicit throwIfAborted() call is safe and ensures the process won't proceed if the signal was triggered after variable coercion and validation steps.


497-517: Effective pattern for unifying abort listeners via a shared promise.

This refactor prevents leaks by removing the need for separate calls to registerAbortSignalListener and unifying cleanup in one place. Great improvement!


1006-1008: Properly cleaning up iterator on abort.

Attaching a listener to invoke iterator.return() ensures resources are freed promptly on abort.


1790-1793: Chaining to assertEventStream enforces correct event-stream validation.

The error handling callback correctly re-throws a located error.


1796-1796: Refactored explicit call to assertEventStream.

This succinctly checks both abort signal and the returned iterable before proceeding.


1802-1807: Graceful check of signal before validating the event stream.

The logic handles pre-aborted signals and invalid values consistently.


1824-1824: Auto-calling iterator.return() on abort.

Well done ensuring the subscription is promptly finalized when aborted.


2160-2161: Racing signalPromise ensures immediate halt on signal abort.

This prevents any further waiting for other promises, aligning with the updated abort handling pattern.


57-57:

✅ Verification successful

Ensure @whatwg-node/promise-helpers is properly declared as a dependency.

The new import is fine. Confirm that it's included in your package manifest and locked at a stable version.


🏁 Script executed:

#!/bin/bash
# Searching the package.json files for the "promise-helpers" dependency
rg '"@whatwg-node/promise-helpers":' package.json

Length of output: 51


I'll re-run the dependency search more broadly to ensure we check all package manifest files in the repository. The initial script only looked for a strict match in the root-level package.json, which might not cover cases where the dependency is declared in another package.json.

Try this extended search:

If this search still produces no output, please verify manually whether the dependency is declared in the appropriate package.json (e.g., in the "packages/executor" folder or the root manifest) so that it's properly locked at a stable version.

Action Required:

  • Confirm that @whatwg-node/promise-helpers is declared as a dependency in the relevant package manifest (either the root package.json or the specific package's manifest).
  • If it’s missing, please add it with a stable version lock.

🏁 Script executed:

#!/bin/bash
# Recursively search all package.json files for the dependency string
fd package.json -x grep -H "@whatwg-node/promise-helpers" {}

Length of output: 506


Dependency Declaration Confirmed

The dependency @whatwg-node/promise-helpers is declared in the package manifests, including in the packages/executor/package.json file with version "^1.0.0". No further action is needed.

@ardatan ardatan merged commit 90a717e into master Mar 13, 2025
18 checks passed
@ardatan ardatan deleted the avoid-using branch March 13, 2025 12:48
ardatan added a commit that referenced this pull request Sep 22, 2025
…handle listeners inside the execution context (#6977)

* fix(executor): do not use leaking `registerAbortSignalListener`, and handle listeners inside the execution context

* lets go
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.

4 participants