From 7f7eda318b0ff463fc697f06c6984483105f2396 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Mon, 5 Aug 2024 22:10:47 +0530 Subject: [PATCH] Nc fix/throttler logging (#9154) * fix: avoid spamming throttler logs * fix: key & remaining ttl --------- Co-authored-by: mertmit --- .../global-exception.filter.ts | 61 +++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/packages/nocodb/src/filters/global-exception/global-exception.filter.ts b/packages/nocodb/src/filters/global-exception/global-exception.filter.ts index 352b4e160f..05c0fb46d0 100644 --- a/packages/nocodb/src/filters/global-exception/global-exception.filter.ts +++ b/packages/nocodb/src/filters/global-exception/global-exception.filter.ts @@ -1,9 +1,12 @@ import { Catch, Logger, NotFoundException, Optional } from '@nestjs/common'; import { InjectSentry, SentryService } from '@ntegral/nestjs-sentry'; import { ThrottlerException } from '@nestjs/throttler'; +import hash from 'object-hash'; import { NcErrorType } from 'nocodb-sdk'; import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common'; import type { Request, Response } from 'express'; +import NocoCache from '~/cache/NocoCache'; +import { CacheGetType } from '~/utils/globals'; import { AjvError, BadRequest, @@ -70,11 +73,59 @@ export class GlobalExceptionFilter implements ExceptionFilter { this.logError(exception, request); if (exception instanceof ThrottlerException) { - this.logger.warn( - `${exception.message}, Path : ${request.path}, Workspace ID : ${ - (request as any).ncWorkspaceId - }, Project ID : ${(request as any).ncBaseId}`, - ); + const key = hash({ + ip: request.ip, + baseId: (request as any).ncBaseId, + workspaceId: (request as any).ncWorkspaceId, + path: request.path, + }); + + const cacheKey = `throttler:${key}`; + + NocoCache.get(cacheKey, CacheGetType.TYPE_OBJECT) + .then((data) => { + if (!data) { + this.logger.warn( + `ThrottlerException: ${exception.message}, Path: ${ + request.path + }, Workspace ID: ${(request as any).ncWorkspaceId}, Base ID: ${ + (request as any).ncBaseId + }`, + ); + + NocoCache.setExpiring( + cacheKey, + { value: true, count: 1, timestamp: Date.now() }, + 300, + ).catch((err) => { + this.logger.error(err); + }); + } else { + data.count += 1; + + const ttlInSeconds = Math.floor( + (data.timestamp + 300000 - Date.now()) / 1000, + ); + + NocoCache.setExpiring(cacheKey, data, ttlInSeconds).catch((err) => { + this.logger.error(err); + }); + + // log every 50th request in last 5 minutes after first + if (data.count % 50 === 0) { + this.logger.warn( + `ThrottlerException: ${exception.message}, Path: ${ + request.path + }, Workspace ID: ${(request as any).ncWorkspaceId}, Base ID: ${ + (request as any).ncBaseId + }, Requests in last 5 minutes: ${data.count}`, + ); + } + } + }) + .catch((err) => { + this.logger.error(err); + }); } // if sso error then redirect to ui with error in query parameter