Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0a6e199
#RI-3751-add command execution time
AmirAllayarovSofteq Nov 2, 2022
090efb6
#RI-3751-fix createCommandExecutions return
AmirAllayarovSofteq Nov 2, 2022
9667a0a
#RI-3751-update execution time calculation
AmirAllayarovSofteq Nov 2, 2022
7755c51
Merge branch 'feature/RI-2831_browser-redisearch' into feature/RI-375…
Nov 2, 2022
dab3a3e
Merge pull request #1353 from RedisInsight/feature/RI-2831_browser-re…
egor-zalenski Nov 2, 2022
b13a451
Merge branch 'feature/RI-3751_command_execution_time' of https://gith…
Nov 2, 2022
414afb7
#RI-3751-update css
AmirAllayarovSofteq Nov 2, 2022
8aaec4a
Merge branch 'feature/RI-3751_command_execution_time' of https://gith…
AmirAllayarovSofteq Nov 2, 2022
7f3fd29
#RI-3751-truncate milleseconds
AmirAllayarovSofteq Nov 2, 2022
9280bb9
#RI-3751 - update execution time calculation
AmirAllayarovSofteq Nov 3, 2022
ac7bb16
#RI-3751-update styles
AmirAllayarovSofteq Nov 3, 2022
dea8ff2
Merge branch 'feature/RI-3751_command_execution_time' of https://gith…
Nov 3, 2022
d3fa3dc
#RI-3773-update styles
AmirAllayarovSofteq Nov 4, 2022
8991889
#RI-3773-update styles
AmirAllayarovSofteq Nov 4, 2022
2b940de
Merge branch 'feature/RI-3751_command_execution_time' of https://gith…
Nov 4, 2022
e1ad246
#RI-3751-fix worbench IT
AmirAllayarovSofteq Nov 4, 2022
b504ff4
#RI-3751-add test
AmirAllayarovSofteq Nov 7, 2022
f2236f2
#RI-3751-fix test
AmirAllayarovSofteq Nov 7, 2022
f6a2fac
#RI-3751-fix test
AmirAllayarovSofteq Nov 7, 2022
04149fd
Merge branch 'feature/RI-3751_command_execution_time' of https://gith…
Nov 7, 2022
f1808ac
create execution time e2e tests
Nov 7, 2022
61a29e7
Merge pull request #1374 from RedisInsight/e2e/feature/RI-3751_execut…
AlenaSY Nov 8, 2022
3956428
Merge branch 'main' into feature/RI-3751_command_execution_time
AlenaSY Nov 8, 2022
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,24 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class workbenchExecutionTime1667368983699 implements MigrationInterface {
name = 'workbenchExecutionTime1667368983699'

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, 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") SELECT "id", "databaseId", "command", "result", "role", "nodeOptions", "encryption", "createdAt", "mode", "resultsMode", "summary" 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") `);
}

public async down(queryRunner: QueryRunner): Promise<void> {
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, 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") SELECT "id", "databaseId", "command", "result", "role", "nodeOptions", "encryption", "createdAt", "mode", "resultsMode", "summary" 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 @@ -19,6 +19,7 @@ import { workbenchMode1660664717573 } from './1660664717573-workbench-mode';
import { workbenchGroupMode1663093411715 } from './1663093411715-workbench-group-mode';
import { databaseAnalysis1664785208236 } from './1664785208236-database-analysis';
import { databaseAnalysisExpirationGroups1664886479051 } from './1664886479051-database-analysis-expiration-groups';
import { workbenchExecutionTime1667368983699 } from './1667368983699-workbench-execution-time';

export default [
initialMigration1614164490968,
Expand All @@ -42,4 +43,5 @@ export default [
workbenchGroupMode1663093411715,
databaseAnalysis1664785208236,
databaseAnalysisExpirationGroups1664886479051,
workbenchExecutionTime1667368983699,
];
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import {
IsEnum, IsArray, IsNotEmptyObject, IsOptional, ArrayNotEmpty, ValidateNested,
IsEnum, IsArray, IsNotEmptyObject, IsDefined, IsOptional, IsString, ArrayNotEmpty, ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';
import { ClusterSingleNodeOptions } from 'src/modules/cli/dto/cli.dto';
Expand All @@ -9,11 +9,13 @@ import { ClusterNodeRole, RunQueryMode, ResultsMode } from './create-command-exe
export class CreateCommandExecutionsDto {
@ApiProperty({
isArray: true,
type: String,
description: 'Redis commands',
})
@IsArray()
@ArrayNotEmpty()
@Type(() => String)
@IsDefined()
@IsString({ each: true })
commands: string[];

@ApiPropertyOptional({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ export class CommandExecutionEntity {
@Column({ nullable: true })
encryption: string;

@Column({ nullable: true })
executionTime?: number;

@CreateDateColumn()
@Index()
createdAt: Date;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ export class CommandExecution {
@Expose()
createdAt: Date;

@ApiPropertyOptional({
description: 'Workbench command execution time',
type: Number,
})
@Expose()
executionTime?: number;

constructor(partial: Partial<CommandExecution> = {}) {
Object.assign(this, partial);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class CommandExecutionProvider {
*/
async createMany(commandExecutions: Partial<CommandExecution>[]): Promise<CommandExecution[]> {
// todo: limit by 30 max to insert
let entities = await Promise.all(commandExecutions.map(async (commandExecution, idx) => {
let entities = await Promise.all(commandExecutions.map(async (commandExecution) => {
const entity = plainToClass(CommandExecutionEntity, commandExecution);

// Do not store command execution result that exceeded limitation
Expand Down Expand Up @@ -61,6 +61,7 @@ export class CommandExecutionProvider {
result: commandExecutions[idx].result,
nodeOptions: commandExecutions[idx].nodeOptions,
summary: commandExecutions[idx].summary,
executionTime: commandExecutions[idx].executionTime,
},
)),
);
Expand Down Expand Up @@ -95,6 +96,7 @@ export class CommandExecutionProvider {
'e.mode',
'e.summary',
'e.resultsMode',
'e.executionTime',
])
.orderBy('e.createdAt', 'DESC')
.limit(WORKBENCH_CONFIG.maxItemsPerDb)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,10 @@ describe('WorkbenchService', () => {

describe('createCommandExecution', () => {
it('should successfully execute command and save it', async () => {
expect(await service.createCommandExecution(mockClientOptions, mockCreateCommandExecutionDto))
.toEqual(mockCommandExecutionToRun);
const result = await service.createCommandExecution(mockClientOptions, mockCreateCommandExecutionDto);
// can't predict execution time
expect(result).toMatchObject(mockCommandExecutionToRun);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toMatchObject nice! didn't know this useful feature :)

expect(result.executionTime).toBeGreaterThan(0);
});
it('should save result as unsupported command message', async () => {
workbenchCommandsExecutor.sendCommand.mockResolvedValueOnce(mockCommandExecutionResults);
Expand Down
13 changes: 13 additions & 0 deletions redisinsight/api/src/modules/workbench/workbench.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ export class WorkbenchService {
},
];
} else {
const startCommandExecutionTime = process.hrtime.bigint();
commandExecution.result = await this.commandsExecutor.sendCommand(clientOptions, { ...dto, command });
const endCommandExecutionTime = process.hrtime.bigint();
commandExecution.executionTime = Math.round((Number(endCommandExecutionTime - startCommandExecutionTime) / 1000));
}

return commandExecution;
Expand All @@ -67,6 +70,9 @@ export class WorkbenchService {
...dto,
databaseId: clientOptions.instanceId,
};
let executionTimeInNanoseconds = BigInt(0);

const startCommandExecutionTime = process.hrtime.bigint();

const executionResults = await Promise.all(commands.map(async (singleCommand) => {
const command = multilineCommandToOneLine(singleCommand);
Expand All @@ -79,9 +85,16 @@ export class WorkbenchService {
});
}
const result = await this.commandsExecutor.sendCommand(clientOptions, { ...dto, command });
const endCommandExecutionTime = process.hrtime.bigint();

executionTimeInNanoseconds += (endCommandExecutionTime - startCommandExecutionTime);
return ({ ...result[0], command });
}));

if (Number(executionTimeInNanoseconds) !== 0) {
commandExecution.executionTime = Math.round(Number(executionTimeInNanoseconds) / 1000);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that using hrtime() is a correct way here.
At first it is legacy function - https://nodejs.org/api/process.html#processhrtimetime. If you want to use use node:process then you should look into hrtime.bugint()
Anothe thing here that you get nanoseconds and then you've added additional check and division to get milliseconds and after all you are rounding value. Why? Will it be more easy to use Data.now() and calculate difference between it? Simple it could be achieved in 2 rows const start = Date.now() and commandExecution.executionTime = Date.now() - statrt. That is it.

const successCommands = executionResults.filter(
(command) => command.status === CommandExecutionStatus.Success,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const responseSchema = Joi.object().keys({
mode: Joi.string().required(),
summary: Joi.string().allow(null),
resultsMode: Joi.string().allow(null),
executionTime: Joi.number().allow(null),
nodeOptions: Joi.object().keys({
host: Joi.string().required(),
port: Joi.number().required(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const responseSchema = Joi.array().items(Joi.object().keys({
mode: Joi.string().required(),
summary: Joi.string().allow(null),
resultsMode: Joi.string().allow(null),
executionTime: Joi.number().allow(null),
nodeOptions: Joi.object().keys({
host: Joi.string().required(),
port: Joi.number().required(),
Expand Down
Loading