- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 4.8k
 
feat: Add options to skip automatic creation of internal database indexes on server start #9897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add options to skip automatic creation of internal database indexes on server start #9897
Conversation
| 
           🚀 Thanks for opening this pull request!  | 
    
          
📝 WalkthroughWalkthroughAdds seven new boolean DatabaseOptions to control automatic index creation; DatabaseController now conditionally creates user/role/token indexes based on those flags; MongoStorageAdapter removes these Parse-specific options from its internal mongo options copy so they are not forwarded to the MongoDB client. Changes
 Sequence Diagram(s)sequenceDiagram
  participant Start as Server Init
  participant DB as DatabaseController
  participant Opts as databaseOptions
  participant Adapter as MongoStorageAdapter
  participant Mongo as MongoDB Client
  rect rgba(0,128,96,0.06)
    Note right of Start: Startup
  end
  Start->>DB: initialize indexes
  DB->>Opts: read createIndexUser* / createIndexRoleName flags
  alt createIndex flag true
    DB->>Adapter: ensureIndex(collection, spec)
    Adapter->>Mongo: createIndex(spec)
    Mongo-->>Adapter: index created
    Adapter-->>DB: success
  else createIndex flag false
    DB-->>DB: skip index creation
  end
  Start->>Adapter: construct with mongoOptions
  Adapter-->>Adapter: remove Parse-specific keys from internal _mongoOptions (including new createIndex* flags)
  Adapter->>Mongo: connect(using filtered _mongoOptions)
    Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–30 minutes 
 Possibly related PRs
 Suggested reviewers
 Pre-merge checks and finishing touches❌ Failed checks (1 warning)
 ✅ Passed checks (2 passed)
 ✨ Finishing touches
 🧪 Generate unit tests (beta)
 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. Comment   | 
    
          ✅ Snyk checks have passed. No issues have been found so far.
 💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/Adapters/Storage/Mongo/MongoStorageAdapter.js (1)
157-174: Option filtering looks good; consider allowlisting + add a small test.Deleting server-only flags from
this._mongoOptions(not mutating the original) is correct. To avoid future leaks as more flags are added, consider switching to an allowlist of known Mongo driver options, or at least add a unit test that asserts thesecreateIndex*flags are not passed toMongoClient.connect.src/Options/docs.js (1)
246-253: Clarify interaction withenableCollationCaseComparison.For the case-insensitive email/username index flags, note that Parse Server skips creating these when
enableCollationCaseComparison: true(see DatabaseController). Add a sentence to avoid confusion.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/Adapters/Storage/Mongo/MongoStorageAdapter.js(1 hunks)src/Controllers/DatabaseController.js(1 hunks)src/Options/Definitions.js(1 hunks)src/Options/docs.js(1 hunks)src/Options/index.js(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/Options/Definitions.js (1)
resources/buildConfigDefinitions.js (1)
parsers(12-12)
🪛 Biome (2.1.2)
src/Options/index.js
[error] 635-635: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 638-638: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 641-641: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 644-644: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 647-647: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 650-650: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 653-653: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
⏰ 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). (13)
- GitHub Check: Node 18
 - GitHub Check: MongoDB 8, ReplicaSet
 - GitHub Check: MongoDB 6, ReplicaSet
 - GitHub Check: Node 20
 - GitHub Check: MongoDB 7, ReplicaSet
 - GitHub Check: Redis Cache
 - GitHub Check: PostgreSQL 16, PostGIS 3.5
 - GitHub Check: PostgreSQL 15, PostGIS 3.5
 - GitHub Check: PostgreSQL 18, PostGIS 3.6
 - GitHub Check: PostgreSQL 17, PostGIS 3.5
 - GitHub Check: PostgreSQL 15, PostGIS 3.4
 - GitHub Check: PostgreSQL 15, PostGIS 3.3
 - GitHub Check: Docker Build
 
🔇 Additional comments (2)
src/Options/index.js (1)
635-655: Flow props fine; verify linter parsing and default propagation.
- The
 ?booleanadditions match existing Flow style, but static analysis flagged parse errors. Ensure Biome is configured to parse/ignore Flow in this file or exclude it, otherwise CI may fail.- Confirm the defaults from
 Definitions.jsactually populateoptions.databaseOptionsat runtime; otherwiseundefinedwould skip index creation inadvertently.src/Options/Definitions.js (1)
1104-1152: Definitions look consistent; add coverage to lock defaults.Env keys/help/defaults for
createIndex*match docs and intent. Please add a small config parsing test asserting these default totruewhen unset so index creation remains on by default.
There was a problem hiding this 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 (2)
spec/MongoStorageAdapter.spec.js (2)
667-805: Consider refactoring to reduce duplication.All 14 test cases follow an identical pattern. You could reduce duplication with a parameterized test helper:
async function testIndexOption(optionName, collectionName, indexName, shouldCreate) { await reconfigureServer({ databaseAdapter: undefined, databaseURI, databaseOptions: { [optionName]: shouldCreate }, }); const indexes = await getIndexes(collectionName); const matcher = shouldCreate ? 'toBeDefined' : 'toBeUndefined'; expect(indexes.find(idx => idx.name === indexName))[matcher](); }Then call it with test data:
const testCases = [ ['createIndexUsername', '_User', 'username_1'], ['createIndexEmail', '_User', 'email_1'], // ... etc ]; testCases.forEach(([option, collection, index]) => { it(`should skip ${index} when ${option} is false`, async () => { await testIndexOption(option, collection, index, false); }); it(`should create ${index} when ${option} is true`, async () => { await testIndexOption(option, collection, index, true); }); });
667-805: Add tests for default behavior.The test suite verifies explicit
trueandfalsevalues but doesn't test what happens when these options are omitted entirely. Consider adding test cases that verify the default behavior (likelytruefor backward compatibility) whendatabaseOptionsis not specified or when individual options are undefined.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
spec/MongoStorageAdapter.spec.js(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 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/MongoStorageAdapter.spec.js
🧬 Code graph analysis (1)
spec/MongoStorageAdapter.spec.js (2)
spec/helper.js (4)
MongoStorageAdapter(58-58)databaseURI(66-66)Parse(4-4)reconfigureServer(180-214)src/Config.js (1)
Config(35-768)
⏰ 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). (13)
- GitHub Check: PostgreSQL 17, PostGIS 3.5
 - GitHub Check: PostgreSQL 15, PostGIS 3.5
 - GitHub Check: PostgreSQL 18, PostGIS 3.6
 - GitHub Check: PostgreSQL 16, PostGIS 3.5
 - GitHub Check: PostgreSQL 15, PostGIS 3.3
 - GitHub Check: Node 18
 - GitHub Check: Docker Build
 - GitHub Check: Node 20
 - GitHub Check: Redis Cache
 - GitHub Check: PostgreSQL 15, PostGIS 3.4
 - GitHub Check: MongoDB 7, ReplicaSet
 - GitHub Check: MongoDB 8, ReplicaSet
 - GitHub Check: MongoDB 6, ReplicaSet
 
🔇 Additional comments (2)
spec/MongoStorageAdapter.spec.js (2)
653-656: LGTM: Test suite setup is clean.The beforeEach hook properly ensures test isolation by cleaning up before each test case.
658-665: LGTM: Helper function handles collection existence gracefully.The function appropriately returns an empty array when the collection doesn't exist, avoiding potential errors.
          Codecov Report❌ Patch coverage is  
 Additional details and impacted files@@           Coverage Diff           @@
##            alpha    #9897   +/-   ##
=======================================
  Coverage   92.99%   92.99%           
=======================================
  Files         187      187           
  Lines       15170    15177    +7     
  Branches      177      177           
=======================================
+ Hits        14107    14114    +7     
  Misses       1051     1051           
  Partials       12       12           ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
  | 
    
There was a problem hiding this 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
♻️ Duplicate comments (1)
src/Controllers/DatabaseController.js (1)
1741-1801: Back-compat preserved; consider a tiny helper + log when skipping.Current
!== falsechecks are correct. A smallenabled(k)helper reduces repetition; optional info logs help ops understand which indexes were intentionally skipped.Example:
- const databaseOptions = this.options.databaseOptions || {}; + const databaseOptions = this.options.databaseOptions || {}; + const enabled = k => databaseOptions[k] !== false; - if (databaseOptions.createIndexUserUsername !== false) { + if (enabled('createIndexUserUsername')) { await this.adapter.ensureUniqueness('_User', requiredUserFields, ['username']).catch(error => { logger.warn('Unable to ensure uniqueness for usernames: ', error); throw error; }); } @@ - if (!this.options.enableCollationCaseComparison) { - if (databaseOptions.createIndexUserUsernameCaseInsensitive !== false) { + if (!this.options.enableCollationCaseComparison) { + if (enabled('createIndexUserUsernameCaseInsensitive')) { await this.adapter .ensureIndex('_User', requiredUserFields, ['username'], 'case_insensitive_username', true) .catch(error => { logger.warn('Unable to create case insensitive username index: ', error); throw error; }); } - if (databaseOptions.createIndexUserEmailCaseInsensitive !== false) { + if (enabled('createIndexUserEmailCaseInsensitive')) { await this.adapter .ensureIndex('_User', requiredUserFields, ['email'], 'case_insensitive_email', true) .catch(error => { logger.warn('Unable to create case insensitive email index: ', error); throw error; }); } } @@ - if (databaseOptions.createIndexUserEmail !== false) { + if (enabled('createIndexUserEmail')) { await this.adapter.ensureUniqueness('_User', requiredUserFields, ['email']).catch(error => { logger.warn('Unable to ensure uniqueness for user email addresses: ', error); throw error; }); } @@ - if (databaseOptions.createIndexUserEmailVerifyToken !== false) { + if (enabled('createIndexUserEmailVerifyToken')) { @@ - if (databaseOptions.createIndexUserPasswordResetToken !== false) { + if (enabled('createIndexUserPasswordResetToken')) { @@ - if (databaseOptions.createIndexRoleName !== false) { + if (enabled('createIndexRoleName')) {Optional (not shown):
logger.info('Skipping <index> creation due to configuration flag');
🧹 Nitpick comments (4)
src/Options/docs.js (1)
246-252: Clarify uniqueness and collation behavior in docs.
- Username/email flags create a unique index (singular), not “indexes”.
 - Case-insensitive indexes are not created when enableCollationCaseComparison is true. Please document.
 Apply this minimal doc tweak:
- * @property {Boolean} createIndexUserEmail Set to `true` to automatically create indexes on the email field of the _User collection on server start. Set to `false` to skip index creation. Default is `true`.<br><br>⚠️ When setting this option to `false` to manually create the index, keep in mind that the otherwise automatically created index may change in the future to be optimized for the internal usage by Parse Server. + * @property {Boolean} createIndexUserEmail Set to `true` to automatically create a unique index on the email field of the _User collection on server start. Set to `false` to skip index creation. Default is `true`.<br><br>⚠️ When setting this option to `false` to manually create the index, keep in mind that the otherwise automatically created index may change in the future to be optimized for the internal usage by Parse Server. - * @property {Boolean} createIndexUserEmailCaseInsensitive Set to `true` to automatically create a case-insensitive index on the email field of the _User collection on server start. Set to `false` to skip index creation. Default is `true`.<br><br>⚠️ When setting this option to `false` to manually create the index, keep in mind that the otherwise automatically created index may change in the future to be optimized for the internal usage by Parse Server. + * @property {Boolean} createIndexUserEmailCaseInsensitive Set to `true` to automatically create a case-insensitive index on the email field of the _User collection on server start. Set to `false` to skip index creation. Default is `true`.<br><br>Note: This index is not created when `enableCollationCaseComparison` is `true`.<br><br>⚠️ When setting this option to `false` to manually create the index, keep in mind that the otherwise automatically created index may change in the future to be optimized for the internal usage by Parse Server. - * @property {Boolean} createIndexUserUsername Set to `true` to automatically create indexes on the username field of the _User collection on server start. Set to `false` to skip index creation. Default is `true`.<br><br>⚠️ When setting this option to `false` to manually create the index, keep in mind that the otherwise automatically created index may change in the future to be optimized for the internal usage by Parse Server. + * @property {Boolean} createIndexUserUsername Set to `true` to automatically create a unique index on the username field of the _User collection on server start. Set to `false` to skip index creation. Default is `true`.<br><br>⚠️ When setting this option to `false` to manually create the index, keep in mind that the otherwise automatically created index may change in the future to be optimized for the internal usage by Parse Server. - * @property {Boolean} createIndexUserUsernameCaseInsensitive Set to `true` to automatically create a case-insensitive index on the username field of the _User collection on server start. Set to `false` to skip index creation. Default is `true`.<br><br>⚠️ When setting this option to `false` to manually create the index, keep in mind that the otherwise automatically created index may change in the future to be optimized for the internal usage by Parse Server. + * @property {Boolean} createIndexUserUsernameCaseInsensitive Set to `true` to automatically create a case-insensitive index on the username field of the _User collection on server start. Set to `false` to skip index creation. Default is `true`.<br><br>Note: This index is not created when `enableCollationCaseComparison` is `true`.<br><br>⚠️ When setting this option to `false` to manually create the index, keep in mind that the otherwise automatically created index may change in the future to be optimized for the internal usage by Parse Server.src/Adapters/Storage/Mongo/MongoStorageAdapter.js (1)
157-174: Good guard: don’t forward Parse-only flags to Mongo client.Looks correct and avoids leaking unsupported options. Optional: extract the omit list to a shared constant to prevent drift and aid testing.
Add a constant:
+// Parse Server-specific options to omit when passing to MongoClient +const OMITTED_PARSE_MONGO_OPTIONS = [ + 'enableSchemaHooks', + 'schemaCacheTtl', + 'maxTimeMS', + 'disableIndexFieldValidation', + 'createIndexUserUsername', + 'createIndexUserUsernameCaseInsensitive', + 'createIndexUserEmail', + 'createIndexUserEmailCaseInsensitive', + 'createIndexUserEmailVerifyToken', + 'createIndexUserPasswordResetToken', + 'createIndexRoleName', +];Then use it here:
- for (const key of [ - 'enableSchemaHooks', - 'schemaCacheTtl', - 'maxTimeMS', - 'disableIndexFieldValidation', - 'createIndexUserUsername', - 'createIndexUserUsernameCaseInsensitive', - 'createIndexUserEmail', - 'createIndexUserEmailCaseInsensitive', - 'createIndexUserEmailVerifyToken', - 'createIndexUserPasswordResetToken', - 'createIndexRoleName', - ]) { + for (const key of OMITTED_PARSE_MONGO_OPTIONS) { delete this._mongoOptions[key]; }spec/MongoStorageAdapter.spec.js (1)
653-826: Add a collation-interaction test (prevents false positives).Case-insensitive indexes are intentionally skipped when enableCollationCaseComparison is true; cover this.
Apply this test:
@@ describe('index creation options', () => { @@ it('should create all indexes by default when options are undefined', async () => { @@ }); + + it('should not create case-insensitive indexes when enableCollationCaseComparison is true', async () => { + await reconfigureServer({ + databaseAdapter: undefined, + databaseURI, + enableCollationCaseComparison: true, + databaseOptions: { + createIndexUserUsernameCaseInsensitive: true, + createIndexUserEmailCaseInsensitive: true, + }, + }); + const userIndexes = await getIndexes('_User'); + expect(userIndexes.find(idx => idx.name === 'case_insensitive_username')).toBeUndefined(); + expect(userIndexes.find(idx => idx.name === 'case_insensitive_email')).toBeUndefined(); + });Optional: also assert
unique === trueforusername_1andemail_1when enabled.If you want, I can add the uniqueness assertions and run a quick check to ensure index specs expose
unique: true.src/Options/Definitions.js (1)
1104-1152: Generated file: ensure help text matches source and uniqueness semantics.This file is generated. After updating src/Options/index.js JSDoc to say “a unique index” for username/email and adding the collation note, please regenerate so this help mirrors the source. Don’t hand-edit here.
Run the generator you normally use (e.g., resources/buildConfigDefinitions.js) to refresh this file after updating comments in src/Options/index.js.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
spec/MongoStorageAdapter.spec.js(1 hunks)src/Adapters/Storage/Mongo/MongoStorageAdapter.js(1 hunks)src/Controllers/DatabaseController.js(1 hunks)src/Options/Definitions.js(1 hunks)src/Options/docs.js(1 hunks)src/Options/index.js(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/Options/Definitions.js (1)
resources/buildConfigDefinitions.js (1)
parsers(12-12)
spec/MongoStorageAdapter.spec.js (2)
spec/helper.js (3)
databaseURI(66-66)Parse(4-4)reconfigureServer(180-214)src/Config.js (1)
Config(35-768)
🪛 Biome (2.1.2)
src/Options/index.js
[error] 635-635: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 638-638: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 641-641: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 644-644: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 647-647: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 650-650: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
[error] 653-653: Expected a statement but instead found '?'.
Expected a statement here.
(parse)
🔇 Additional comments (1)
src/Options/index.js (1)
635-655: Fix Biome configuration to support Flow types; verify documentation accuracy for index behavior.The core issue is confirmed: Biome 2.1.2 (v2.1.2) cannot parse Flow type syntax in
src/Options/index.js, generating 238 errors throughout the file. The unique field is username and the case sensitive unique index remains and is important, and the case insensitive index is not unique.Action items:
Exclude Flow files from Biome or configure it to use a Flow-capable parser (e.g., Babel+Flow), as Biome does not support Flow's optional type syntax (
?boolean,?string, etc.).Verify JSDoc claims at lines 635–655:
- The documentation refers to "indexes" but should clarify whether the index created is unique (for email and username) or non-unique (for case-insensitive variants). The case insensitive index is not unique.
 - Verify whether case-insensitive index creation is conditionally skipped when
 enableCollationCaseComparisonis enabled; if so, add clarifying notes to those JSDoc blocks.Example doc refinement (conditionally, pending verification):
- /* Set to `true` to automatically create indexes on the email field... + /* Set to `true` to automatically create a unique index on the email field... - /* Set to `true` to automatically create a case-insensitive index on the email field... + /* Set to `true` to automatically create a case-insensitive index on the email field... + Note: This index is not created when `enableCollationCaseComparison` is `true`.
# [8.3.0-alpha.14](8.3.0-alpha.13...8.3.0-alpha.14) (2025-11-01) ### Features * Add options to skip automatic creation of internal database indexes on server start ([#9897](#9897)) ([ea91aca](ea91aca))
| 
           🎉 This change has been released in version 8.3.0-alpha.14  | 
    
# [8.3.0](8.2.5...8.3.0) (2025-11-01) ### Bug Fixes * Error in `afterSave` trigger for `Parse.Role` due to `name` field ([#9883](#9883)) ([eb052d8](eb052d8)) * Indexes `_email_verify_token` for email verification and `_perishable_token` password reset are not created automatically ([#9893](#9893)) ([62dd3c5](62dd3c5)) * Security upgrade to parse 7.0.1 ([#9877](#9877)) ([abfa94c](abfa94c)) * Server URL verification before server is ready ([#9882](#9882)) ([178bd5c](178bd5c)) * Stale data read in validation query on `Parse.Object` update causes inconsistency between validation read and subsequent update write operation ([#9859](#9859)) ([f49efaf](f49efaf)) * Warning logged when setting option `databaseOptions.disableIndexFieldValidation` ([#9880](#9880)) ([1815b01](1815b01)) ### Features * Add option `keepUnknownIndexes` to retain indexes which are not specified in schema ([#9857](#9857)) ([89fad46](89fad46)) * Add options to skip automatic creation of internal database indexes on server start ([#9897](#9897)) ([ea91aca](ea91aca)) * Add Parse Server option `verifyServerUrl` to disable server URL verification on server launch ([#9881](#9881)) ([b298ccc](b298ccc)) * Add regex option `u` for unicode support in `Parse.Query.matches` for MongoDB ([#9867](#9867)) ([7cb962a](7cb962a)) * Add request context middleware for config and dependency injection in hooks ([#8480](#8480)) ([64f104e](64f104e)) * Add support for Postgres 18 ([#9870](#9870)) ([d275c18](d275c18)) * Allow returning objects in `Parse.Cloud.beforeFind` without invoking database query ([#9770](#9770)) ([0b47407](0b47407)) * Disable index-field validation to create index for fields that don't yet exist ([#8137](#8137)) ([1b23475](1b23475))
| 
           🎉 This change has been released in version 8.3.0  | 
    
Pull Request
Issue
Automatic index creation on server launch prevents to optimize the created index for example as compound index.
Approach
Added seven new configuration options to control automatic database index creation for username, email, email case-insensitive variants, email verification tokens, password reset tokens, and role names. Administrators can now individually enable or disable each index type during server initialization.
Tasks
Summary by CodeRabbit
New Features
Bug Fixes / Behavior
Documentation
Tests