diff --git a/redisinsight/api/src/constants/telemetry-events.ts b/redisinsight/api/src/constants/telemetry-events.ts index 7435b96aa5..33da5b2034 100644 --- a/redisinsight/api/src/constants/telemetry-events.ts +++ b/redisinsight/api/src/constants/telemetry-events.ts @@ -51,4 +51,8 @@ export enum TelemetryEvents { PubSubMessagePublished = 'PUBSUB_MESSAGE_PUBLISHED', PubSubChannelSubscribed = 'PUBSUB_CHANNEL_SUBSCRIBED', PubSubChannelUnsubscribed = 'PUBSUB_CHANNEL_UNSUBSCRIBED', + + // Bulk Actions + BulkActionsStarted = 'BULK_ACTIONS_STARTED', + BulkActionsStopped = 'BULK_ACTIONS_STOPPED', } diff --git a/redisinsight/api/src/modules/bulk-actions/bulk-actions-analytics.service.ts b/redisinsight/api/src/modules/bulk-actions/bulk-actions-analytics.service.ts new file mode 100644 index 0000000000..946d251a23 --- /dev/null +++ b/redisinsight/api/src/modules/bulk-actions/bulk-actions-analytics.service.ts @@ -0,0 +1,79 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { TelemetryEvents } from 'src/constants'; +import { TelemetryBaseService } from 'src/modules/shared/services/base/telemetry.base.service'; +import { CommandExecutionStatus } from 'src/modules/cli/dto/cli.dto'; +import { RedisError, ReplyError } from 'src/models'; +import { IBulkActionOverview } from 'src/modules/bulk-actions/interfaces/bulk-action-overview.interface'; + +export interface IExecResult { + response: any; + status: CommandExecutionStatus; + error?: RedisError | ReplyError | Error, +} + +@Injectable() +export class BulkActionsAnalyticsService extends TelemetryBaseService { + private events: Map = new Map(); + + constructor(protected eventEmitter: EventEmitter2) { + super(eventEmitter); + this.events.set(TelemetryEvents.BulkActionsStarted, this.sendActionStarted.bind(this)); + this.events.set(TelemetryEvents.BulkActionsStopped, this.sendActionStopped.bind(this)); + } + + sendActionStarted(overview: IBulkActionOverview): void { + try { + this.sendEvent( + TelemetryEvents.BulkActionsStarted, + { + databaseId: overview.databaseId, + type: overview.type, + duration: overview.duration, + filter: { + match: overview.filter.match === '*' ? '*' : 'PATTERN', + type: overview.filter.type, + }, + progress: { + scanned: overview.progress.scanned, + total: overview.progress.total, + }, + }, + ); + } catch (e) { + // continue regardless of error + } + } + + sendActionStopped(overview: IBulkActionOverview): void { + try { + this.sendEvent( + TelemetryEvents.BulkActionsStopped, + { + databaseId: overview.databaseId, + type: overview.type, + duration: overview.duration, + filter: { + match: overview.filter.match === '*' ? '*' : 'PATTERN', + type: overview.filter.type, + }, + progress: { + scanned: overview.progress.scanned, + total: overview.progress.total, + }, + summary: { + processed: overview.summary.processed, + succeed: overview.summary.succeed, + failed: overview.summary.failed, + }, + }, + ); + } catch (e) { + // continue regardless of error + } + } + + getEventsEmitters(): Map { + return this.events; + } +} diff --git a/redisinsight/api/src/modules/bulk-actions/bulk-actions.module.ts b/redisinsight/api/src/modules/bulk-actions/bulk-actions.module.ts index 267f260458..21659f06ed 100644 --- a/redisinsight/api/src/modules/bulk-actions/bulk-actions.module.ts +++ b/redisinsight/api/src/modules/bulk-actions/bulk-actions.module.ts @@ -3,6 +3,7 @@ import { BulkActionsService } from 'src/modules/bulk-actions/bulk-actions.servic import { BulkActionsProvider } from 'src/modules/bulk-actions/providers/bulk-actions.provider'; import { BulkActionsGateway } from 'src/modules/bulk-actions/bulk-actions.gateway'; import { SharedModule } from 'src/modules/shared/shared.module'; +import { BulkActionsAnalyticsService } from 'src/modules/bulk-actions/bulk-actions-analytics.service'; @Module({ imports: [ @@ -12,6 +13,7 @@ import { SharedModule } from 'src/modules/shared/shared.module'; BulkActionsGateway, BulkActionsService, BulkActionsProvider, + BulkActionsAnalyticsService, ], }) export class BulkActionsModule {} diff --git a/redisinsight/api/src/modules/bulk-actions/bulk-actions.service.spec.ts b/redisinsight/api/src/modules/bulk-actions/bulk-actions.service.spec.ts index 024875681e..509c07584d 100644 --- a/redisinsight/api/src/modules/bulk-actions/bulk-actions.service.spec.ts +++ b/redisinsight/api/src/modules/bulk-actions/bulk-actions.service.spec.ts @@ -11,6 +11,7 @@ import { CreateBulkActionDto } from 'src/modules/bulk-actions/dto/create-bulk-ac import { BulkActionFilter } from 'src/modules/bulk-actions/models/bulk-action-filter'; import { BulkAction } from 'src/modules/bulk-actions/models/bulk-action'; import { BulkActionsService } from 'src/modules/bulk-actions/bulk-actions.service'; +import { BulkActionsAnalyticsService } from 'src/modules/bulk-actions/bulk-actions-analytics.service'; export const mockSocket1 = new MockedSocket(); mockSocket1.id = '1'; @@ -38,6 +39,7 @@ const mockCreateBulkActionDto = Object.assign(new CreateBulkActionDto(), { const mockBulkAction = new BulkAction( mockCreateBulkActionDto.id, + mockCreateBulkActionDto.databaseId, mockCreateBulkActionDto.type, mockBulkActionFilter, mockSocket1, @@ -65,6 +67,13 @@ describe('BulkActionsService', () => { abortUsersBulkActions: jest.fn().mockReturnValue(2), }), }, + { + provide: BulkActionsAnalyticsService, + useFactory: () => ({ + sendActionStarted: jest.fn(), + sendActionStopped: jest.fn(), + }), + }, ], }).compile(); diff --git a/redisinsight/api/src/modules/bulk-actions/bulk-actions.service.ts b/redisinsight/api/src/modules/bulk-actions/bulk-actions.service.ts index 85484759fb..203eac7f67 100644 --- a/redisinsight/api/src/modules/bulk-actions/bulk-actions.service.ts +++ b/redisinsight/api/src/modules/bulk-actions/bulk-actions.service.ts @@ -1,18 +1,24 @@ +import { Socket } from 'socket.io'; import { Injectable } from '@nestjs/common'; import { BulkActionsProvider } from 'src/modules/bulk-actions/providers/bulk-actions.provider'; import { CreateBulkActionDto } from 'src/modules/bulk-actions/dto/create-bulk-action.dto'; import { BulkActionIdDto } from 'src/modules/bulk-actions/dto/bulk-action-id.dto'; -import { Socket } from 'socket.io'; +import { BulkActionsAnalyticsService } from 'src/modules/bulk-actions/bulk-actions-analytics.service'; @Injectable() export class BulkActionsService { constructor( private readonly bulkActionsProvider: BulkActionsProvider, + private readonly analyticsService: BulkActionsAnalyticsService, ) {} async create(dto: CreateBulkActionDto, socket: Socket) { const bulkAction = await this.bulkActionsProvider.create(dto, socket); - return bulkAction.getOverview(); + const overview = bulkAction.getOverview(); + + this.analyticsService.sendActionStarted(overview); + + return overview; } async get(dto: BulkActionIdDto) { @@ -22,7 +28,11 @@ export class BulkActionsService { async abort(dto: BulkActionIdDto) { const bulkAction = await this.bulkActionsProvider.abort(dto.id); - return bulkAction.getOverview(); + const overview = bulkAction.getOverview(); + + this.analyticsService.sendActionStopped(overview); + + return overview; } disconnect(socketId: string) { diff --git a/redisinsight/api/src/modules/bulk-actions/interfaces/bulk-action-overview.interface.ts b/redisinsight/api/src/modules/bulk-actions/interfaces/bulk-action-overview.interface.ts index da0c3b4d59..2efcadbdec 100644 --- a/redisinsight/api/src/modules/bulk-actions/interfaces/bulk-action-overview.interface.ts +++ b/redisinsight/api/src/modules/bulk-actions/interfaces/bulk-action-overview.interface.ts @@ -5,6 +5,7 @@ import { IBulkActionFilterOverview } from './bulk-action-filter-overview.interfa export interface IBulkActionOverview { id: string, + databaseId: string, duration: number, type: BulkActionType, status: BulkActionStatus, diff --git a/redisinsight/api/src/modules/bulk-actions/models/bulk-action.spec.ts b/redisinsight/api/src/modules/bulk-actions/models/bulk-action.spec.ts index 2093d93f8e..cfc158181a 100644 --- a/redisinsight/api/src/modules/bulk-actions/models/bulk-action.spec.ts +++ b/redisinsight/api/src/modules/bulk-actions/models/bulk-action.spec.ts @@ -77,6 +77,7 @@ describe('AbstractBulkActionSimpleRunner', () => { bulkAction = new BulkAction( mockCreateBulkActionDto.id, + mockCreateBulkActionDto.databaseId, mockCreateBulkActionDto.type, mockBulkActionFilter, mockSocket, diff --git a/redisinsight/api/src/modules/bulk-actions/models/bulk-action.ts b/redisinsight/api/src/modules/bulk-actions/models/bulk-action.ts index c768e91703..2cb4619a14 100644 --- a/redisinsight/api/src/modules/bulk-actions/models/bulk-action.ts +++ b/redisinsight/api/src/modules/bulk-actions/models/bulk-action.ts @@ -12,6 +12,8 @@ export class BulkAction implements IBulkAction { private readonly id: string; + private readonly databaseId: string; + private startTime: number = Date.now(); private endTime: number; @@ -28,8 +30,9 @@ export class BulkAction implements IBulkAction { private readonly debounce: Function; - constructor(id, type, filter, socket) { + constructor(id, databaseId, type, filter, socket) { this.id = id; + this.databaseId = databaseId; this.type = type; this.filter = filter; this.socket = socket; @@ -133,6 +136,7 @@ export class BulkAction implements IBulkAction { return { id: this.id, + databaseId: this.databaseId, type: this.type, duration: (this.endTime || Date.now()) - this.startTime, status: this.status, diff --git a/redisinsight/api/src/modules/bulk-actions/models/runners/abstract.bulk-action.runner.spec.ts b/redisinsight/api/src/modules/bulk-actions/models/runners/abstract.bulk-action.runner.spec.ts index d6e21ad3c1..2724bb6800 100644 --- a/redisinsight/api/src/modules/bulk-actions/models/runners/abstract.bulk-action.runner.spec.ts +++ b/redisinsight/api/src/modules/bulk-actions/models/runners/abstract.bulk-action.runner.spec.ts @@ -34,6 +34,7 @@ describe('AbstractBulkActionRunner', () => { beforeEach(() => { bulkAction = new BulkAction( mockCreateBulkActionDto.id, + mockCreateBulkActionDto.databaseId, mockCreateBulkActionDto.type, mockBulkActionFilter, mockSocket, diff --git a/redisinsight/api/src/modules/bulk-actions/models/runners/simple/abstract.bulk-action.simple.runner.spec.ts b/redisinsight/api/src/modules/bulk-actions/models/runners/simple/abstract.bulk-action.simple.runner.spec.ts index 9553cc769e..fb738064fb 100644 --- a/redisinsight/api/src/modules/bulk-actions/models/runners/simple/abstract.bulk-action.simple.runner.spec.ts +++ b/redisinsight/api/src/modules/bulk-actions/models/runners/simple/abstract.bulk-action.simple.runner.spec.ts @@ -43,6 +43,7 @@ describe('AbstractBulkActionSimpleRunner', () => { bulkAction = new BulkAction( mockCreateBulkActionDto.id, + mockCreateBulkActionDto.databaseId, mockCreateBulkActionDto.type, mockBulkActionFilter, mockSocket, diff --git a/redisinsight/api/src/modules/bulk-actions/models/runners/simple/delete.bulk-action.simple.runner.spec.ts b/redisinsight/api/src/modules/bulk-actions/models/runners/simple/delete.bulk-action.simple.runner.spec.ts index 8307b4a72f..aac0627b1a 100644 --- a/redisinsight/api/src/modules/bulk-actions/models/runners/simple/delete.bulk-action.simple.runner.spec.ts +++ b/redisinsight/api/src/modules/bulk-actions/models/runners/simple/delete.bulk-action.simple.runner.spec.ts @@ -25,6 +25,7 @@ const mockCreateBulkActionDto = { const bulkAction = new BulkAction( mockCreateBulkActionDto.id, + mockCreateBulkActionDto.databaseId, mockCreateBulkActionDto.type, mockBulkActionFilter, mockSocket, diff --git a/redisinsight/api/src/modules/bulk-actions/providers/bulk-actions.provider.ts b/redisinsight/api/src/modules/bulk-actions/providers/bulk-actions.provider.ts index ba50844951..98041adb2b 100644 --- a/redisinsight/api/src/modules/bulk-actions/providers/bulk-actions.provider.ts +++ b/redisinsight/api/src/modules/bulk-actions/providers/bulk-actions.provider.ts @@ -33,7 +33,7 @@ export class BulkActionsProvider { throw new Error('You already have bulk action with such id'); } - const bulkAction = new BulkAction(dto.id, dto.type, dto.filter, socket); + const bulkAction = new BulkAction(dto.id, dto.databaseId, dto.type, dto.filter, socket); this.bulkActions.set(dto.id, bulkAction);