Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class workbenchAndAnalysisDbIndex1673934231410 implements MigrationInterface {
name = 'workbenchAndAnalysisDbIndex1673934231410'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX "IDX_5cd90dd6def1fd7c521e53fb2c"`);
await queryRunner.query(`CREATE TABLE "temporary_command_execution" ("id" varchar PRIMARY KEY NOT NULL, "databaseId" varchar NOT NULL, "command" text NOT NULL, "result" text NOT NULL, "role" varchar, "nodeOptions" varchar, "encryption" varchar, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "mode" varchar, "resultsMode" varchar, "summary" varchar, "executionTime" integer, "db" integer, CONSTRAINT "FK_ea8adfe9aceceb79212142206b8" FOREIGN KEY ("databaseId") REFERENCES "database_instance" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_command_execution"("id", "databaseId", "command", "result", "role", "nodeOptions", "encryption", "createdAt", "mode", "resultsMode", "summary", "executionTime") SELECT "id", "databaseId", "command", "result", "role", "nodeOptions", "encryption", "createdAt", "mode", "resultsMode", "summary", "executionTime" FROM "command_execution"`);
await queryRunner.query(`DROP TABLE "command_execution"`);
await queryRunner.query(`ALTER TABLE "temporary_command_execution" RENAME TO "command_execution"`);
await queryRunner.query(`CREATE INDEX "IDX_5cd90dd6def1fd7c521e53fb2c" ON "command_execution" ("createdAt") `);
await queryRunner.query(`DROP INDEX "IDX_d174a8edc2201d6c5781f0126a"`);
await queryRunner.query(`DROP INDEX "IDX_fdd0daeb4d8f226cf1ff79bebb"`);
await queryRunner.query(`CREATE TABLE "temporary_database_analysis" ("id" varchar PRIMARY KEY NOT NULL, "databaseId" varchar NOT NULL, "filter" blob, "delimiter" varchar NOT NULL, "progress" blob, "totalKeys" blob, "totalMemory" blob, "topKeysNsp" blob, "topMemoryNsp" blob, "topKeysLength" blob, "topKeysMemory" blob, "encryption" varchar, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "expirationGroups" blob, "db" integer, CONSTRAINT "FK_d174a8edc2201d6c5781f0126ae" FOREIGN KEY ("databaseId") REFERENCES "database_instance" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "temporary_database_analysis"("id", "databaseId", "filter", "delimiter", "progress", "totalKeys", "totalMemory", "topKeysNsp", "topMemoryNsp", "topKeysLength", "topKeysMemory", "encryption", "createdAt", "expirationGroups") SELECT "id", "databaseId", "filter", "delimiter", "progress", "totalKeys", "totalMemory", "topKeysNsp", "topMemoryNsp", "topKeysLength", "topKeysMemory", "encryption", "createdAt", "expirationGroups" FROM "database_analysis"`);
await queryRunner.query(`DROP TABLE "database_analysis"`);
await queryRunner.query(`ALTER TABLE "temporary_database_analysis" RENAME TO "database_analysis"`);
await queryRunner.query(`CREATE INDEX "IDX_d174a8edc2201d6c5781f0126a" ON "database_analysis" ("databaseId") `);
await queryRunner.query(`CREATE INDEX "IDX_fdd0daeb4d8f226cf1ff79bebb" ON "database_analysis" ("createdAt") `);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX "IDX_fdd0daeb4d8f226cf1ff79bebb"`);
await queryRunner.query(`DROP INDEX "IDX_d174a8edc2201d6c5781f0126a"`);
await queryRunner.query(`ALTER TABLE "database_analysis" RENAME TO "temporary_database_analysis"`);
await queryRunner.query(`CREATE TABLE "database_analysis" ("id" varchar PRIMARY KEY NOT NULL, "databaseId" varchar NOT NULL, "filter" blob, "delimiter" varchar NOT NULL, "progress" blob, "totalKeys" blob, "totalMemory" blob, "topKeysNsp" blob, "topMemoryNsp" blob, "topKeysLength" blob, "topKeysMemory" blob, "encryption" varchar, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "expirationGroups" blob, CONSTRAINT "FK_d174a8edc2201d6c5781f0126ae" FOREIGN KEY ("databaseId") REFERENCES "database_instance" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "database_analysis"("id", "databaseId", "filter", "delimiter", "progress", "totalKeys", "totalMemory", "topKeysNsp", "topMemoryNsp", "topKeysLength", "topKeysMemory", "encryption", "createdAt", "expirationGroups") SELECT "id", "databaseId", "filter", "delimiter", "progress", "totalKeys", "totalMemory", "topKeysNsp", "topMemoryNsp", "topKeysLength", "topKeysMemory", "encryption", "createdAt", "expirationGroups" FROM "temporary_database_analysis"`);
await queryRunner.query(`DROP TABLE "temporary_database_analysis"`);
await queryRunner.query(`CREATE INDEX "IDX_fdd0daeb4d8f226cf1ff79bebb" ON "database_analysis" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_d174a8edc2201d6c5781f0126a" ON "database_analysis" ("databaseId") `);
await queryRunner.query(`DROP INDEX "IDX_5cd90dd6def1fd7c521e53fb2c"`);
await queryRunner.query(`ALTER TABLE "command_execution" RENAME TO "temporary_command_execution"`);
await queryRunner.query(`CREATE TABLE "command_execution" ("id" varchar PRIMARY KEY NOT NULL, "databaseId" varchar NOT NULL, "command" text NOT NULL, "result" text NOT NULL, "role" varchar, "nodeOptions" varchar, "encryption" varchar, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "mode" varchar, "resultsMode" varchar, "summary" varchar, "executionTime" integer, CONSTRAINT "FK_ea8adfe9aceceb79212142206b8" FOREIGN KEY ("databaseId") REFERENCES "database_instance" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
await queryRunner.query(`INSERT INTO "command_execution"("id", "databaseId", "command", "result", "role", "nodeOptions", "encryption", "createdAt", "mode", "resultsMode", "summary", "executionTime") SELECT "id", "databaseId", "command", "result", "role", "nodeOptions", "encryption", "createdAt", "mode", "resultsMode", "summary", "executionTime" FROM "temporary_command_execution"`);
await queryRunner.query(`DROP TABLE "temporary_command_execution"`);
await queryRunner.query(`CREATE INDEX "IDX_5cd90dd6def1fd7c521e53fb2c" ON "command_execution" ("createdAt") `);
}

}
2 changes: 2 additions & 0 deletions redisinsight/api/migration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { workbenchExecutionTime1667368983699 } from './1667368983699-workbench-e
import { database1667477693934 } from './1667477693934-database';
import { databaseNew1670252337342 } from './1670252337342-database-new';
import { sshOptions1673035852335 } from './1673035852335-ssh-options';
import { workbenchAndAnalysisDbIndex1673934231410 } from './1673934231410-workbench_and_analysis_db';

export default [
initialMigration1614164490968,
Expand Down Expand Up @@ -50,4 +51,5 @@ export default [
database1667477693934,
databaseNew1670252337342,
sshOptions1673035852335,
workbenchAndAnalysisDbIndex1673934231410,
];
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class DatabaseAnalysisService {

const analysis = plainToClass(DatabaseAnalysis, await this.analyzer.analyze({
databaseId: clientMetadata.databaseId,
db: client?.options?.db || 0,
...dto,
progress,
}, [].concat(...scanResults.map((nodeResult) => nodeResult.keys))));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, JoinColumn, Index,
} from 'typeorm';
import { IsInt, Min } from 'class-validator';
import { Expose, Transform } from 'class-transformer';
import { DataAsJsonString } from 'src/common/decorators';
import { DatabaseEntity } from 'src/modules/database/entities/database.entity';
Expand Down Expand Up @@ -118,6 +119,12 @@ export class DatabaseAnalysisEntity {
@Column({ nullable: true })
encryption: string;

@Column({ nullable: true })
@Expose()
@IsInt()
@Min(0)
db?: number;

@CreateDateColumn()
@Index()
@Expose()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { NspSummary } from 'src/modules/database-analysis/models/nsp-summary';
import { Key } from 'src/modules/database-analysis/models/key';
import { IsInt, IsOptional, Min } from 'class-validator';
import { Expose, Type } from 'class-transformer';
import { SimpleSummary } from 'src/modules/database-analysis/models/simple-summary';
import { ApiProperty } from '@nestjs/swagger';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { ScanFilter } from 'src/modules/database-analysis/models/scan-filter';
import { AnalysisProgress } from 'src/modules/database-analysis/models/analysis-progress';
import { SumGroup } from 'src/modules/database-analysis/models/sum-group';
Expand Down Expand Up @@ -114,4 +115,14 @@ export class DatabaseAnalysis {
@Expose()
@Type(() => SumGroup)
expirationGroups: SumGroup[];

@ApiPropertyOptional({
description: 'Logical database number.',
type: Number,
})
@Expose()
@IsInt()
@Min(0)
@IsOptional()
db?: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import { PartialType, PickType } from '@nestjs/swagger';
import { DatabaseAnalysis } from 'src/modules/database-analysis/models/database-analysis';

export class ShortDatabaseAnalysis extends PartialType(
PickType(DatabaseAnalysis, ['id', 'createdAt'] as const),
PickType(DatabaseAnalysis, ['id', 'createdAt', 'db'] as const),
) {}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class DatabaseAnalysisProvider {
const entities = await this.repository
.createQueryBuilder('a')
.where({ databaseId })
.select(['a.id', 'a.createdAt'])
.select(['a.id', 'a.createdAt', 'a.db'])
.orderBy('a.createdAt', 'DESC')
.limit(DATABASE_ANALYSIS_CONFIG.maxItemsPerDb)
.getMany();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
import { DatabaseEntity } from 'src/modules/database/entities/database.entity';
import { RunQueryMode, ResultsMode } from 'src/modules/workbench/dto/create-command-execution.dto';
import { Expose, Transform } from 'class-transformer';
import { IsInt, Min } from 'class-validator';

@Entity('command_execution')
export class CommandExecutionEntity {
Expand Down Expand Up @@ -84,6 +85,12 @@ export class CommandExecutionEntity {
@Expose()
executionTime?: number;

@Column({ nullable: true })
@Expose()
@IsInt()
@Min(0)
db?: number;

@CreateDateColumn()
@Index()
@Expose()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsDefined } from 'class-validator';
import { IsDefined, IsInt, IsOptional, Min } from 'class-validator';
import { CommandExecutionResult } from 'src/modules/workbench/models/command-execution-result';
import { ClusterNodeRole, RunQueryMode, ResultsMode } from 'src/modules/workbench/dto/create-command-execution.dto';
import { ClusterSingleNodeOptions } from 'src/modules/cli/dto/cli.dto';
Expand Down Expand Up @@ -117,6 +117,16 @@ export class CommandExecution {
@Expose()
executionTime?: number;

@ApiPropertyOptional({
description: 'Logical database number.',
type: Number,
})
@Expose()
@IsInt()
@Min(0)
@IsOptional()
db?: number;

constructor(partial: Partial<CommandExecution> = {}) {
Object.assign(this, partial);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export class CommandExecutionProvider {
'e.summary',
'e.resultsMode',
'e.executionTime',
'e.db',
])
.orderBy('e.createdAt', 'DESC')
.limit(WORKBENCH_CONFIG.maxItemsPerDb)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const mockCommandExecutionResults: CommandExecutionResult[] = [
const mockCommandExecutionToRun: CommandExecution = new CommandExecution({
...mockCreateCommandExecutionDto,
databaseId: mockDatabase.id,
db: 0,
});

const mockCommandExecution: CommandExecution = new CommandExecution({
Expand Down Expand Up @@ -177,6 +178,12 @@ describe('WorkbenchService', () => {
expect(result).toMatchObject(mockCommandExecutionToRun);
expect(result.executionTime).toBeGreaterThan(0);
});
it('should save db index', async () => {
const db = 2
const result = await service.createCommandExecution(mockWorkbenchClientMetadata, mockCreateCommandExecutionDto, db);
expect(result).toMatchObject({...mockCommandExecutionToRun, db});
expect(result.db).toBe(db);
});
it('should save result as unsupported command message', async () => {
workbenchCommandsExecutor.sendCommand.mockResolvedValueOnce(mockCommandExecutionResults);

Expand All @@ -188,6 +195,7 @@ describe('WorkbenchService', () => {

expect(await service.createCommandExecution(mockWorkbenchClientMetadata, dto)).toEqual({
...dto,
db: 0,
databaseId: mockWorkbenchClientMetadata.databaseId,
result: [
{
Expand Down
14 changes: 11 additions & 3 deletions redisinsight/api/src/modules/workbench/workbench.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,16 @@ export class WorkbenchService {
*
* @param clientMetadata
* @param dto
* @param db
*/
async createCommandExecution(
clientMetadata: ClientMetadata,
dto: CreateCommandExecutionDto,
db: number = 0,
): Promise<Partial<CommandExecution>> {
const commandExecution: Partial<CommandExecution> = {
...omit(dto, 'commands'),
db,
databaseId: clientMetadata.databaseId,
};

Expand Down Expand Up @@ -63,16 +66,19 @@ export class WorkbenchService {
* @param clientMetadata
* @param dto
* @param commands
* @param db
* @param onlyErrorResponse
*/
async createCommandsExecution(
clientMetadata: ClientMetadata,
dto: Partial<CreateCommandExecutionDto>,
commands: string[],
db: number = 0,
onlyErrorResponse: boolean = false,
): Promise<Partial<CommandExecution>> {
const commandExecution: Partial<CommandExecution> = {
...dto,
db,
databaseId: clientMetadata.databaseId,
};
let executionTimeInNanoseconds = BigInt(0);
Expand Down Expand Up @@ -134,17 +140,19 @@ export class WorkbenchService {
): Promise<CommandExecution[]> {
// todo: handle concurrent client creation on RedisModule side
// temporary workaround. Just create client before any command execution precess
await this.databaseConnectionService.getOrCreateClient(clientMetadata);
const client = await this.databaseConnectionService.getOrCreateClient(clientMetadata);

if (dto.resultsMode === ResultsMode.GroupMode || dto.resultsMode === ResultsMode.Silent) {
return this.commandExecutionProvider.createMany(
[await this.createCommandsExecution(clientMetadata, dto, dto.commands, dto.resultsMode === ResultsMode.Silent)],
[await this.createCommandsExecution(clientMetadata, dto, dto.commands, client?.options?.db, dto.resultsMode === ResultsMode.Silent)],
);
}
// todo: rework to support pipeline
// prepare and execute commands
const commandExecutions = await Promise.all(
dto.commands.map(async (command) => await this.createCommandExecution(clientMetadata, { ...dto, command })),
dto.commands.map(
async (command) => await this.createCommandExecution(clientMetadata, { ...dto, command }, client?.options?.db),
),
);

// save history
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe('GET /databases/:id/analysis/:id', () => {
expect(body).to.deep.eq({
id: constants.TEST_DATABASE_ANALYSIS_ID_1,
databaseId: constants.TEST_INSTANCE_ID,
db: constants.TEST_DATABASE_ANALYSIS_DB_1,
createdAt: constants.TEST_DATABASE_ANALYSIS_CREATED_AT_1.toISOString(),
delimiter: constants.TEST_DATABASE_ANALYSIS_DELIMITER_1,
filter: constants.TEST_DATABASE_ANALYSIS_FILTER_1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const endpoint = (
const responseSchema = Joi.array().items(Joi.object({
createdAt: Joi.date().required(),
id: Joi.string().required(),
db: Joi.number().integer().allow(null),
})).required().max(5);
const mainCheckFn = getMainCheckFn(endpoint);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ describe('POST /databases/:instanceId/analysis', () => {
expect(body.topKeysLength.length).to.gt(0);
expect(body.topKeysMemory.length).to.gt(0);
expect(body.expirationGroups.length).to.gt(0);
expect(body.db).to.gte(0);
},
after: async () => {
expect(await repository.count()).to.eq(5);
Expand All @@ -64,6 +65,7 @@ describe('POST /databases/:instanceId/analysis', () => {
expect(body.topKeysLength.length).to.gt(0);
expect(body.topKeysMemory.length).to.gt(0);
expect(body.expirationGroups.length).to.gt(0);
expect(body.db).to.gte(0);
},
after: async () => {
expect(await repository.count()).to.eq(5);
Expand Down Expand Up @@ -142,6 +144,7 @@ describe('POST /databases/:instanceId/analysis', () => {
expect(body.expirationGroups[0].label).to.eq('No Expiry');
expect(body.expirationGroups[0].total).to.gt(0);
expect(body.expirationGroups[0].threshold).to.eq(0);
expect(body.db).to.eq(0);
},
after: async () => {
expect(await repository.count()).to.eq(5);
Expand Down
1 change: 1 addition & 0 deletions redisinsight/api/test/api/database-analysis/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const analysisSchema = Joi.object().keys({
}).required(),
totalKeys: typedTotalSchema.required(),
totalMemory: typedTotalSchema.required(),
db: Joi.number().integer().allow(null),
topKeysNsp: Joi.array().items(nspSummarySchema).required().max(15),
topMemoryNsp: Joi.array().items(nspSummarySchema).required().max(15),
topKeysLength: Joi.array().items(keySchema).required().max(15),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const responseSchema = Joi.object().keys({
port: Joi.number().required(),
enableRedirection: Joi.boolean().required(),
}).allow(null),
db: Joi.number().integer().allow(null),
createdAt: Joi.date().required(),
}).required();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const responseSchema = Joi.array().items(Joi.object().keys({
port: Joi.number().required(),
enableRedirection: Joi.boolean().required(),
}).allow(null),
db: Joi.number().integer().allow(null),
createdAt: Joi.date().required(),
})).required().max(30);

Expand Down
Loading