diff --git a/lib/worker.ts b/lib/worker.ts index 15438ebe..b07f2111 100644 --- a/lib/worker.ts +++ b/lib/worker.ts @@ -173,6 +173,13 @@ export abstract class Worker { ); } + /** + * Forced clears worker cache + */ + public clearCache(): void { + this.cache.flushAll(); + } + /** * Create cache controller instance */ diff --git a/workers/grouper/src/index.ts b/workers/grouper/src/index.ts index dfc7236f..b071555b 100644 --- a/workers/grouper/src/index.ts +++ b/workers/grouper/src/index.ts @@ -26,6 +26,16 @@ export default class GrouperWorker extends Worker { */ public readonly type: string = pkg.workerType; + /** + * Contains event grouphashes by its catcher type and event title as keys + * + * @example + * { + * 'grouper:Hawk client catcher test': '7e2b961c35b915dcbe2704e144e8d2c3517e2c5281a5de4403c0c58978b435a0' + * } + */ + private static cachedHashValues: Record = {}; + /** * Database Controller */ @@ -37,9 +47,15 @@ export default class GrouperWorker extends Worker { * @param task - worker task to create hash */ private static getUniqueEventHash(task: GroupWorkerTask): string { - return crypto.createHmac('sha256', process.env.EVENT_SECRET) - .update(task.catcherType + task.event.title) - .digest('hex'); + const computedHashValueCacheKey = `${task.catcherType}:${task.event.title}`; + + if (!this.cachedHashValues[computedHashValueCacheKey]) { + this.cachedHashValues[computedHashValueCacheKey] = crypto.createHmac('sha256', process.env.EVENT_SECRET) + .update(task.catcherType + task.event.title) + .digest('hex'); + } + + return this.cachedHashValues[computedHashValueCacheKey]; } /** @@ -47,6 +63,7 @@ export default class GrouperWorker extends Worker { */ public async start(): Promise { await this.db.connect(); + this.prepareCache(); await super.start(); } @@ -168,11 +185,14 @@ export default class GrouperWorker extends Worker { if (isUserFromOriginalEvent) { return false; } else { - const repetition = await this.db.getConnection().collection(`repetitions:${task.projectId}`) - .findOne({ - groupHash: existedEvent.groupHash, - 'payload.user.id': eventUser.id, - }); + const repetitionCacheKey = `repetitions:${task.projectId}:${existedEvent.groupHash}:${eventUser.id}`; + const repetition = await this.cache.get(repetitionCacheKey, async () => { + return this.db.getConnection().collection(`repetitions:${task.projectId}`) + .findOne({ + groupHash: existedEvent.groupHash, + 'payload.user.id': eventUser.id, + }); + }); /** * If there is no repetitions from this user — return true @@ -187,18 +207,21 @@ export default class GrouperWorker extends Worker { * @param projectId - project's identifier * @param query - mongo query string */ - private async getEvent(projectId: string, query): Promise { + private async getEvent(projectId: string, query: Record): Promise { if (!mongodb.ObjectID.isValid(projectId)) { throw new ValidationError('Controller.saveEvent: Project ID is invalid or missed'); } - try { + const eventCacheKey = `${projectId}:${JSON.stringify(query)}`; + + return this.cache.get(eventCacheKey, async () => { return this.db.getConnection() .collection(`events:${projectId}`) - .findOne(query); - } catch (err) { - throw new DatabaseReadWriteError(err); - } + .findOne(query) + .catch((err) => { + throw new DatabaseReadWriteError(err); + }); + }); } /** @@ -279,7 +302,7 @@ export default class GrouperWorker extends Worker { } /** - * saves event at the special aggregation collection + * Saves event at the special aggregation collection * * @param {string} projectId - project's identifier * @param {string} eventHash - event hash diff --git a/workers/grouper/tests/index.test.ts b/workers/grouper/tests/index.test.ts index 5b58e88e..e1d44618 100644 --- a/workers/grouper/tests/index.test.ts +++ b/workers/grouper/tests/index.test.ts @@ -91,7 +91,11 @@ describe('GrouperWorker', () => { repetitionsCollection = connection.db().collection('repetitions:' + testGroupingTask.projectId); }); + /** + * Clears worker cache and mongodb before each test + */ beforeEach(async () => { + worker.clearCache(); await eventsCollection.deleteMany({}); await dailyEventsCollection.deleteMany({}); await repetitionsCollection.deleteMany({});