Skip to content

Conversation

@Moumouls
Copy link
Member

@Moumouls Moumouls commented Nov 10, 2025

Pull Request

Issue

Underload, apollo server can be initialized multiple times. Schema ref should be used as mutex to prevent parallel init, so all _getServer await the same promise ref

Closes: FILL_THIS_OUT

Approach

Using schema as a ref mutex

Tasks

  • Add tests
  • Add changes to documentation (guides, repository pages, code comments)
  • Add security check
  • Add new Parse Error codes to Parse JS SDK

Summary by CodeRabbit

  • Bug Fixes

    • Improved concurrent initialization so parallel requests reuse the same server instance, preventing duplicate server creation during simultaneous access.
  • Tests

    • Added a test that verifies repeated parallel calls to server initialization return the same shared server reference.

@parse-github-assistant
Copy link

I will reformat the title to use the proper commit message syntax.

@parse-github-assistant parse-github-assistant bot changed the title fix: apollo parallel server init under load fix: Apollo parallel server init under load Nov 10, 2025
@parse-github-assistant
Copy link

parse-github-assistant bot commented Nov 10, 2025

🚀 Thanks for opening this pull request!

❌ Please fill out all fields with a placeholder FILL_THIS_OUT. If a field does not apply to the pull request, fill in n/a or delete the line.

@coderabbitai
Copy link

coderabbitai bot commented Nov 10, 2025

📝 Walkthrough

Walkthrough

Adds a concurrency guard and promise-based initialization to ParseGraphQLServer._getServer(), and a test that verifies 100 parallel _getServer() calls resolve to the same server reference.

Changes

Cohort / File(s) Summary
Test
spec/ParseGraphQLServer.spec.js
Adds a test that resets cached server, invokes _getServer() 100 times in parallel, awaits all results, and asserts every result is identical to the first.
Concurrency & Init Logic
src/GraphQL/ParseGraphQLServer.js
Introduces _schemaRefMutex checks and a createServer() factory. Stores a non-awaited server initialization promise in this._server so parallel callers share the same initialization promise; on error, resets _server and _schemaRefMutex.

Sequence Diagram(s)

sequenceDiagram
    participant CallerA
    participant CallerB
    participant ParseGraphQLServer
    participant Mutex
    participant ServerInit

    Note over CallerA,CallerB: Parallel callers request server
    CallerA->>ParseGraphQLServer: _getServer()
    CallerB->>ParseGraphQLServer: _getServer()
    ParseGraphQLServer->>Mutex: check/acquire _schemaRefMutex
    Mutex-->>ParseGraphQLServer: lock/allow creation OR attach to existing promise
    alt first creator
        ParseGraphQLServer->>ServerInit: createServer() (returns promise)
        ServerInit-->>ParseGraphQLServer: resolves with Server instance
        ParseGraphQLServer-->>CallerA: resolves with Server
        ParseGraphQLServer-->>CallerB: resolves with same Server (await same promise)
    else attaching caller
        ParseGraphQLServer-->>CallerB: returns existing init promise
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas to focus:
    • correctness of _schemaRefMutex acquisition/release and race conditions
    • storing non-awaited promise in this._server and its lifecycle (success, failure, retry)
    • error-handling path that resets _server and _schemaRefMutex
    • test robustness and potential flakiness under CI concurrency

Possibly related PRs

Suggested reviewers

  • mtrezza

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The description is incomplete: it lacks a specific issue link (still shows FILL_THIS_OUT), and the Approach section is vague. However, the Issue explanation and technical details are provided. Link the PR to a specific GitHub issue and expand the Approach section with more detailed explanation of the mutex implementation using schema references.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main performance optimization: preventing parallel Apollo Server initialization under concurrent load.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad81073 and 2b203b3.

📒 Files selected for processing (1)
  • src/GraphQL/ParseGraphQLServer.js (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: Tests in the parse-server repository should use promise-based approaches rather than callback patterns with `done()`. Use a pattern where a Promise is created that resolves when the event occurs, then await that promise.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`. The preferred pattern is to create a Promise that resolves when an expected event occurs, then await that Promise.
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1312-1338
Timestamp: 2025-05-04T20:41:05.147Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`.
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: Tests in the parse-server repository should use promise-based approaches rather than callback patterns with `done()`. Use a pattern where a Promise is created that resolves when the event occurs, then await that promise.

Applied to files:

  • src/GraphQL/ParseGraphQLServer.js
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`. The preferred pattern is to create a Promise that resolves when an expected event occurs, then await that Promise.

Applied to files:

  • src/GraphQL/ParseGraphQLServer.js
📚 Learning: 2025-05-04T20:41:05.147Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1312-1338
Timestamp: 2025-05-04T20:41:05.147Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`.

Applied to files:

  • src/GraphQL/ParseGraphQLServer.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: PostgreSQL 17, PostGIS 3.5
  • GitHub Check: PostgreSQL 18, PostGIS 3.6
  • GitHub Check: PostgreSQL 15, PostGIS 3.3
  • GitHub Check: Node 20
  • GitHub Check: Node 22
  • GitHub Check: PostgreSQL 15, PostGIS 3.5
  • GitHub Check: PostgreSQL 15, PostGIS 3.4
  • GitHub Check: PostgreSQL 16, PostGIS 3.5
  • GitHub Check: Node 18
  • GitHub Check: Redis Cache
  • GitHub Check: MongoDB 8, ReplicaSet
  • GitHub Check: MongoDB 6, ReplicaSet
  • GitHub Check: MongoDB 7, ReplicaSet
  • GitHub Check: Docker Build
  • GitHub Check: Code Analysis (javascript)
  • GitHub Check: Benchmarks

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@parseplatformorg
Copy link
Contributor

parseplatformorg commented Nov 10, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@Moumouls
Copy link
Member Author

@Stovedal, thanks for your report, it could lead to a significant performance gain during startup under heavy load.

@Moumouls Moumouls requested a review from mtrezza November 10, 2025 17:30
@Moumouls Moumouls changed the title fix: Apollo parallel server init under load perf: Apollo parallel server init under load Nov 10, 2025
Copy link

@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: 1

🧹 Nitpick comments (1)
spec/ParseGraphQLServer.spec.js (1)

121-134: Good concurrency test; tighten assertions and reset.

Looks solid and reflects the intended mutex behavior. Two nits:

  • Prefer a single assertion using a Set to ensure all references are identical.
  • If the implementation now stores the in-flight promise on _server, resetting server may not cold‑start the path. Consider clearing the correct slot (or adding a tiny helper) to avoid false positives.
-    it('should return same server reference when called 100 times in parallel', async () => {
-      parseGraphQLServer.server = undefined;
+    it('should return same server reference when called 100 times in parallel', async () => {
+      // Ensure cold start regardless of internal field name.
+      parseGraphQLServer.server = undefined;
+      if ('_server' in parseGraphQLServer) parseGraphQLServer._server = undefined;
@@
-      // All resolved servers should be the same reference
-      const firstServer = servers[0];
-      servers.forEach((server, index) => {
-        expect(server).toBe(firstServer);
-      });
+      // All resolved servers should be the same reference
+      const unique = new Set(servers);
+      expect(unique.size).toBe(1);
     });

If feasible, also spy on the underlying server factory to assert it runs once (e.g., spyOn(parseGraphQLServer, '_createServer') or equivalent) to make the test stricter. Based on learnings.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 133660f and ad81073.

📒 Files selected for processing (2)
  • spec/ParseGraphQLServer.spec.js (1 hunks)
  • src/GraphQL/ParseGraphQLServer.js (1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 0
File: :0-0
Timestamp: 2025-11-08T13:46:04.940Z
Learning: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: Tests in the parse-server repository should use promise-based approaches rather than callback patterns with `done()`. Use a pattern where a Promise is created that resolves when the event occurs, then await that promise.

Applied to files:

  • spec/ParseGraphQLServer.spec.js
📚 Learning: 2025-05-04T20:41:05.147Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1312-1338
Timestamp: 2025-05-04T20:41:05.147Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`.

Applied to files:

  • spec/ParseGraphQLServer.spec.js
📚 Learning: 2025-05-09T09:59:06.289Z
Learnt from: mtrezza
Repo: parse-community/parse-server PR: 9445
File: spec/ParseLiveQuery.spec.js:1340-1375
Timestamp: 2025-05-09T09:59:06.289Z
Learning: New tests in the parse-server repository should use async/await with promise-based patterns rather than callback patterns with `done()`. The preferred pattern is to create a Promise that resolves when an expected event occurs, then await that Promise.

Applied to files:

  • spec/ParseGraphQLServer.spec.js
📚 Learning: 2025-04-30T19:31:35.344Z
Learnt from: RahulLanjewar93
Repo: parse-community/parse-server PR: 9744
File: spec/ParseLiveQuery.spec.js:0-0
Timestamp: 2025-04-30T19:31:35.344Z
Learning: In the Parse Server codebase, the functions in QueryTools.js are typically tested through end-to-end behavior tests rather than direct unit tests, even though the functions are exported from the module.

Applied to files:

  • spec/ParseGraphQLServer.spec.js
📚 Learning: 2025-10-16T19:27:05.311Z
Learnt from: Moumouls
Repo: parse-community/parse-server PR: 9883
File: spec/CloudCodeLogger.spec.js:410-412
Timestamp: 2025-10-16T19:27:05.311Z
Learning: In spec/CloudCodeLogger.spec.js, the test "should log cloud function triggers using the silent log level" (around lines 383-420) is known to be flaky and requires the extra `await new Promise(resolve => setTimeout(resolve, 100))` timeout after awaiting `afterSavePromise` for reliability, even though it may appear redundant.

Applied to files:

  • spec/ParseGraphQLServer.spec.js
📚 Learning: 2025-08-27T09:08:34.252Z
Learnt from: EmpiDev
Repo: parse-community/parse-server PR: 9770
File: src/triggers.js:446-454
Timestamp: 2025-08-27T09:08:34.252Z
Learning: When analyzing function signature changes in Parse Server codebase, verify that call sites are actually incorrect before flagging them. Passing tests are a strong indicator that function calls are already properly aligned with new signatures.

Applied to files:

  • spec/ParseGraphQLServer.spec.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Benchmarks

@codecov
Copy link

codecov bot commented Nov 10, 2025

Codecov Report

❌ Patch coverage is 76.92308% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.06%. Comparing base (133660f) to head (2b203b3).

Files with missing lines Patch % Lines
src/GraphQL/ParseGraphQLServer.js 76.92% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            alpha    #9929      +/-   ##
==========================================
- Coverage   93.07%   93.06%   -0.02%     
==========================================
  Files         187      187              
  Lines       15224    15233       +9     
  Branches      177      177              
==========================================
+ Hits        14170    14176       +6     
- Misses       1042     1045       +3     
  Partials       12       12              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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