diff --git a/src/Config.js b/src/Config.js index 812d28c367..9215674950 100644 --- a/src/Config.js +++ b/src/Config.js @@ -558,6 +558,24 @@ export class Config { } else if (typeof databaseOptions.schemaCacheTtl !== 'number') { throw `databaseOptions.schemaCacheTtl must be a number`; } + if (databaseOptions.disableEnsureUsernameCaseInsensitiveIndex === undefined) { + databaseOptions.disableEnsureUsernameCaseInsensitiveIndex = + DatabaseOptions.disableEnsureUsernameCaseInsensitiveIndex.default; + } else if (typeof databaseOptions.disableEnsureUsernameCaseInsensitiveIndex !== 'boolean') { + throw `databaseOptions.disableEnsureUsernameCaseInsensitiveIndex must be a boolean`; + } + if (databaseOptions.disableEnsureEmailCaseInsensitiveIndex === undefined) { + databaseOptions.disableEnsureEmailCaseInsensitiveIndex = + DatabaseOptions.disableEnsureEmailCaseInsensitiveIndex.default; + } else if (typeof databaseOptions.disableEnsureEmailCaseInsensitiveIndex !== 'boolean') { + throw `databaseOptions.disableEnsureEmailCaseInsensitiveIndex must be a boolean`; + } + if (databaseOptions.disableEnsureIdempotencyExpireIndex === undefined) { + databaseOptions.disableEnsureIdempotencyExpireIndex = + DatabaseOptions.disableEnsureIdempotencyExpireIndex.default; + } else if (typeof databaseOptions.disableEnsureIdempotencyExpireIndex !== 'boolean') { + throw `databaseOptions.disableEnsureIdempotencyExpireIndex must be a boolean`; + } } static validateRateLimit(rateLimit) { diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index e3ac5723ab..d0ac38498e 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -1706,30 +1706,33 @@ class DatabaseController { throw error; }); - 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; - }); - 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 (!this.options.databaseOptions?.disableEnsureUsernameCaseInsensitiveIndex) { + 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; + }); + 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; + }); + } await this.adapter.ensureUniqueness('_User', requiredUserFields, ['email']).catch(error => { logger.warn('Unable to ensure uniqueness for user email addresses: ', error); throw error; }); - 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 (!this.options.databaseOptions?.disableEnsureEmailCaseInsensitiveIndex) { + 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; + }); + } await this.adapter.ensureUniqueness('_Role', requiredRoleFields, ['name']).catch(error => { logger.warn('Unable to ensure uniqueness for role name: ', error); @@ -1755,12 +1758,14 @@ class DatabaseController { options = this.idempotencyOptions; options.setIdempotencyFunction = true; } - await this.adapter - .ensureIndex('_Idempotency', requiredIdempotencyFields, ['expire'], 'ttl', false, options) - .catch(error => { - logger.warn('Unable to create TTL index for idempotency expire date: ', error); - throw error; - }); + if (!this.options.databaseOptions.disableEnsureIdempotencyExpireIndex) { + await this.adapter + .ensureIndex('_Idempotency', requiredIdempotencyFields, ['expire'], 'ttl', false, options) + .catch(error => { + logger.warn('Unable to create TTL index for idempotency expire date: ', error); + throw error; + }); + } } await this.adapter.updateSchemaWithIndexes(); } diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index b2f0542256..0c5971977f 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -969,6 +969,27 @@ module.exports.FileUploadOptions = { }, }; module.exports.DatabaseOptions = { + disableEnsureEmailCaseInsensitiveIndex: { + env: 'PARSE_SERVER_DATABASE_DISABLE_ENSURE_EMAIL_CASE_INSENSITIVE_INDEX', + help: + 'Disables behavior to ensure case-insensitive index on field email on _User collection. Set to `true` if using a database not supporting case-insensitive indexes.', + action: parsers.booleanParser, + default: false, + }, + disableEnsureIdempotencyExpireIndex: { + env: 'PARSE_SERVER_DATABASE_DISABLE_ENSURE_IDEMPOTENCY_EXPIRE_INDEX', + help: + 'Disables behavior to ensure time to live index on field expire on _Idempotency collection. Set to `true` if using a database not supporting TTL index on this field.', + action: parsers.booleanParser, + default: false, + }, + disableEnsureUsernameCaseInsensitiveIndex: { + env: 'PARSE_SERVER_DATABASE_DISABLE_ENSURE_USERNAME_CASE_INSENSITIVE_INDEX', + help: + 'Disables behavior to ensure case-insensitive index on field username on _User collection. Set to `true` if using a database not supporting case-insensitive indexes.', + action: parsers.booleanParser, + default: false, + }, enableSchemaHooks: { env: 'PARSE_SERVER_DATABASE_ENABLE_SCHEMA_HOOKS', help: diff --git a/src/Options/docs.js b/src/Options/docs.js index 1ab8c03d58..d4432c6e5c 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -225,6 +225,9 @@ /** * @interface DatabaseOptions + * @property {Boolean} disableEnsureEmailCaseInsensitiveIndex Disables behavior to ensure case-insensitive index on field email on _User collection. Set to `true` if using a database not supporting case-insensitive indexes. + * @property {Boolean} disableEnsureIdempotencyExpireIndex Disables behavior to ensure time to live index on field expire on _Idempotency collection. Set to `true` if using a database not supporting TTL index on this field. + * @property {Boolean} disableEnsureUsernameCaseInsensitiveIndex Disables behavior to ensure case-insensitive index on field username on _User collection. Set to `true` if using a database not supporting case-insensitive indexes. * @property {Boolean} enableSchemaHooks Enables database real-time hooks to update single schema cache. Set to `true` if using multiple Parse Servers instances connected to the same database. Failing to do so will cause a schema change to not propagate to all instances and re-syncing will only happen when the instances restart. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required. * @property {Number} schemaCacheTtl The duration in seconds after which the schema cache expires and will be refetched from the database. Use this option if using multiple Parse Servers instances connected to the same database. A low duration will cause the schema cache to be updated too often, causing unnecessary database reads. A high duration will cause the schema to be updated too rarely, increasing the time required until schema changes propagate to all server instances. This feature can be used as an alternative or in conjunction with the option `enableSchemaHooks`. Default is infinite which means the schema cache never expires. */ diff --git a/src/Options/index.js b/src/Options/index.js index a4d83f94fc..077ea7e610 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -553,6 +553,15 @@ export interface DatabaseOptions { enableSchemaHooks: ?boolean; /* The duration in seconds after which the schema cache expires and will be refetched from the database. Use this option if using multiple Parse Servers instances connected to the same database. A low duration will cause the schema cache to be updated too often, causing unnecessary database reads. A high duration will cause the schema to be updated too rarely, increasing the time required until schema changes propagate to all server instances. This feature can be used as an alternative or in conjunction with the option `enableSchemaHooks`. Default is infinite which means the schema cache never expires. */ schemaCacheTtl: ?number; + /* Disables behavior to ensure case-insensitive index on field username on _User collection. Set to `true` if using a database not supporting case-insensitive indexes. + :DEFAULT: false */ + disableEnsureUsernameCaseInsensitiveIndex: ?boolean; + /* Disables behavior to ensure case-insensitive index on field email on _User collection. Set to `true` if using a database not supporting case-insensitive indexes. + :DEFAULT: false */ + disableEnsureEmailCaseInsensitiveIndex: ?boolean; + /* Disables behavior to ensure time to live index on field expire on _Idempotency collection. Set to `true` if using a database not supporting TTL index on this field. + :DEFAULT: false */ + disableEnsureIdempotencyExpireIndex: ?boolean; } export interface AuthAdapter {