From 36b658298dff4f7b6970a2e358d55328556d63e0 Mon Sep 17 00:00:00 2001 From: Alexey Zinoviev Date: Wed, 2 Oct 2024 14:58:30 +0400 Subject: [PATCH] UBERF-8330: Smarter Mongo indices init for account (#6783) Signed-off-by: Alexey Zinoviev --- server/account/src/collections/mongo.ts | 88 +++++++++++++++++++++++-- server/account/src/utils.ts | 7 ++ 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/server/account/src/collections/mongo.ts b/server/account/src/collections/mongo.ts index f6d77331e6..1da601b0ec 100644 --- a/server/account/src/collections/mongo.ts +++ b/server/account/src/collections/mongo.ts @@ -13,7 +13,7 @@ // limitations under the License. // import { ObjectId as MongoObjectId } from 'mongodb' -import type { Collection, Db, Filter, OptionalUnlessRequiredId, Sort } from 'mongodb' +import type { Collection, CreateIndexesOptions, Db, Filter, OptionalUnlessRequiredId, Sort } from 'mongodb' import type { Data, Version } from '@hcengineering/core' import type { @@ -31,6 +31,12 @@ import type { OtpRecord, UpgradeStatistic } from '../types' +import { isShallowEqual } from '../utils' + +interface MongoIndex { + key: Record + options: CreateIndexesOptions & { name: string } +} export class MongoDbCollection> implements DbCollection { constructor ( @@ -43,7 +49,55 @@ export class MongoDbCollection> implements DbColle } async init (): Promise { - // May be used to create indicex in Mongo + // May be used to create indices in Mongo + } + + /** + * Ensures indices in the collection or creates new if needed. + * Drops all other indices that are not in the list. + * @param indicesToEnsure MongoIndex + */ + async ensureIndices (indicesToEnsure: MongoIndex[]): Promise { + try { + const indices = await this.collection.listIndexes().toArray() + + for (const idx of indices) { + if (idx.key._id !== undefined) { + continue + } + + const isEqualIndex = (ensureIdx: MongoIndex): boolean => { + const { key, options } = ensureIdx + const sameKeys = isShallowEqual(idx.key, key) + + if (!sameKeys) { + return false + } + + const shortIdxOptions = { ...idx } + delete shortIdxOptions.key + delete shortIdxOptions.v + + return isShallowEqual(shortIdxOptions, options) + } + + if (indicesToEnsure.some(isEqualIndex)) { + continue + } + + await this.collection.dropIndex(idx.name) + } + } catch (e: any) { + if (e?.codeName === 'NamespaceNotFound') { + // Nothing to do, new DB + } else { + throw e + } + } + + for (const { key, options } of indicesToEnsure) { + await this.collection.createIndex(key, options) + } } async find (query: Query, sort?: { [P in keyof T]?: 'ascending' | 'descending' }, limit?: number): Promise { @@ -99,7 +153,14 @@ export class AccountMongoDbCollection extends MongoDbCollection impleme } async init (): Promise { - await this.collection.createIndex({ email: 1 }, { unique: true }) + const indicesToEnsure: MongoIndex[] = [ + { + key: { email: 1 }, + options: { unique: true, name: 'hc_account_email_1' } + } + ] + + await this.ensureIndices(indicesToEnsure) } convertToObj (acc: Account): Account { @@ -133,7 +194,26 @@ export class WorkspaceMongoDbCollection extends MongoDbCollection imp } async init (): Promise { - await this.collection.createIndex({ workspace: 1 }, { unique: true }) + // await this.collection.createIndex({ workspace: 1 }, { unique: true }) + + const indicesToEnsure: MongoIndex[] = [ + { + key: { workspace: 1 }, + options: { + unique: true, + name: 'hc_account_workspace_1' + } + }, + { + key: { workspaceUrl: 1 }, + options: { + unique: true, + name: 'hc_account_workspaceUrl_1' + } + } + ] + + await this.ensureIndices(indicesToEnsure) } async countWorkspacesInRegion (region: string, upToVersion?: Data, visitedSince?: number): Promise { diff --git a/server/account/src/utils.ts b/server/account/src/utils.ts index d8e87bb905..f24ad042ac 100644 --- a/server/account/src/utils.ts +++ b/server/account/src/utils.ts @@ -187,3 +187,10 @@ export function areDbIdsEqual (obj1: any, obj2: any): boolean { return obj1 === obj2 } + +export function isShallowEqual (obj1: Record, obj2: Record): boolean { + const keys1 = Object.keys(obj1) + const keys2 = Object.keys(obj2) + + return keys1.length === keys2.length && keys1.every((k) => obj1[k] === obj2[k]) +}