Skip to content

Commit 944de76

Browse files
committed
CR-263: Optimize recommendations lookup and creation
1 parent 117e6bb commit 944de76

File tree

8 files changed

+105
-93
lines changed

8 files changed

+105
-93
lines changed

redisinsight/api/src/__mocks__/database-recommendation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const mockDatabaseRecommendationService = () => ({
3434
create: jest.fn(),
3535
list: jest.fn(),
3636
check: jest.fn(),
37+
checkMulti: jest.fn(),
3738
read: jest.fn(),
3839
});
3940

redisinsight/api/src/modules/browser/keys/keys.service.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -145,21 +145,17 @@ export class KeysService {
145145

146146
const result = await this.keyInfoProvider.getStrategy(type).getInfo(client, key, type);
147147
this.logger.debug('Succeed to get key info', clientMetadata);
148-
this.recommendationService.check(
149-
clientMetadata,
150-
RECOMMENDATION_NAMES.BIG_SETS,
151-
result,
152-
);
153-
this.recommendationService.check(
154-
clientMetadata,
155-
RECOMMENDATION_NAMES.BIG_STRINGS,
156-
result,
157-
);
158-
this.recommendationService.check(
148+
149+
this.recommendationService.checkMulti(
159150
clientMetadata,
160-
RECOMMENDATION_NAMES.COMPRESSION_FOR_LIST,
151+
[
152+
RECOMMENDATION_NAMES.BIG_SETS,
153+
RECOMMENDATION_NAMES.BIG_STRINGS,
154+
RECOMMENDATION_NAMES.COMPRESSION_FOR_LIST,
155+
],
161156
result,
162157
);
158+
163159
return plainToClass(GetKeyInfoResponse, result);
164160
} catch (error) {
165161
this.logger.error('Failed to get key info.', error, clientMetadata);

redisinsight/api/src/modules/database-recommendation/database-recommendation.service.ts

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,38 @@ export class DatabaseRecommendationService {
5252
return this.databaseRecommendationRepository.list({ ...clientMetadata, db });
5353
}
5454

55-
/**
56-
* Check recommendation condition
57-
* @param clientMetadata
58-
* @param recommendationName
59-
* @param data
60-
*/
61-
public async check(
62-
clientMetadata: ClientMetadata,
55+
private async checkRecommendation(
6356
recommendationName: string,
64-
data: any,
57+
exists: boolean,
58+
clientMetadata: ClientMetadata, data: any,
6559
): Promise<DatabaseRecommendation> {
60+
if (exists) {
61+
return null;
62+
}
63+
64+
const recommendation = await this.scanner.determineRecommendation(
65+
clientMetadata.sessionMetadata,
66+
recommendationName,
67+
data,
68+
);
69+
70+
if (recommendation) {
71+
const entity = plainToClass(
72+
DatabaseRecommendation,
73+
{ databaseId: clientMetadata?.databaseId, ...recommendation },
74+
);
75+
76+
return await this.create(clientMetadata, entity);
77+
}
78+
79+
return null;
80+
}
81+
82+
public async checkMulti(
83+
clientMetadata: ClientMetadata,
84+
recommendationNames: string[],
85+
data: any,
86+
): Promise<Map<string, DatabaseRecommendation[]>> {
6687
try {
6788
const newClientMetadata = {
6889
...clientMetadata,
@@ -72,32 +93,40 @@ export class DatabaseRecommendationService {
7293
};
7394
const isRecommendationExist = await this.databaseRecommendationRepository.isExist(
7495
newClientMetadata,
75-
recommendationName,
96+
recommendationNames,
7697
);
77-
if (!isRecommendationExist) {
78-
const recommendation = await this.scanner.determineRecommendation(
79-
clientMetadata.sessionMetadata,
80-
recommendationName,
81-
data,
82-
);
83-
84-
if (recommendation) {
85-
const entity = plainToClass(
86-
DatabaseRecommendation,
87-
{ databaseId: newClientMetadata?.databaseId, ...recommendation },
88-
);
89-
90-
return await this.create(newClientMetadata, entity);
91-
}
92-
}
9398

94-
return null;
99+
const results = await Promise.all(
100+
recommendationNames.map(
101+
(name) => this.checkRecommendation(name, isRecommendationExist[name], newClientMetadata, data),
102+
),
103+
);
104+
105+
return results.reduce((acc, result, idx) => ({
106+
...acc,
107+
[recommendationNames[idx]]: result,
108+
}), {} as Map<string, DatabaseRecommendation[]>);
95109
} catch (e) {
96110
this.logger.warn('Unable to check recommendation', e, clientMetadata);
97111
return null;
98112
}
99113
}
100114

115+
/**
116+
* Check recommendation condition
117+
* @param clientMetadata
118+
* @param recommendationName
119+
* @param data
120+
*/
121+
public async check(
122+
clientMetadata: ClientMetadata,
123+
recommendationName: string,
124+
data: any,
125+
): Promise<DatabaseRecommendation> {
126+
const result = await this.checkMulti(clientMetadata, [recommendationName], data);
127+
return result[recommendationName];
128+
}
129+
101130
/**
102131
* Mark all recommendations as read for particular database
103132
* @param clientMetadata

redisinsight/api/src/modules/database-recommendation/repositories/database-recommendation.repository.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ export abstract class DatabaseRecommendationRepository {
4444
/**
4545
* Check is recommendation already exist in repository
4646
* @param clientMetadata
47-
* @param name
48-
* @return Boolean
47+
* @param names
48+
* @return Map<string, boolean>
4949
*/
50-
abstract isExist(clientMetadata: ClientMetadata, name: string): Promise<boolean>;
50+
abstract isExist(clientMetadata: ClientMetadata, names: string[]): Promise<Map<string, boolean>>;
5151

5252
/**
5353
* Get database recommendation by id

redisinsight/api/src/modules/database-recommendation/repositories/local.database.recommendation.repository.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,17 @@ describe('LocalDatabaseRecommendationRepository', () => {
6464

6565
describe('isExist', () => {
6666
it('should return true when receive database entity', async () => {
67-
expect(await service.isExist(mockClientMetadata, mockRecommendationName)).toEqual(true);
67+
expect(await service.isExist(mockClientMetadata, [mockRecommendationName])).toEqual({ [mockRecommendationName]: true });
6868
});
6969

7070
it('should return false when no database received', async () => {
7171
repository.findOneBy.mockResolvedValueOnce(null);
72-
expect(await service.isExist(mockClientMetadata, mockRecommendationName)).toEqual(false);
72+
expect(await service.isExist(mockClientMetadata, [mockRecommendationName])).toEqual({ [mockRecommendationName]: false });
7373
});
7474

75-
it('should return false when received error', async () => {
75+
it('should return empty object when received error', async () => {
7676
repository.findOneBy.mockRejectedValueOnce(new Error());
77-
expect(await service.isExist(mockClientMetadata, mockRecommendationName)).toEqual(false);
77+
expect(await service.isExist(mockClientMetadata, [mockRecommendationName])).toEqual({});
7878
});
7979
});
8080

redisinsight/api/src/modules/database-recommendation/repositories/local.database.recommendation.repository.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,22 +147,25 @@ export class LocalDatabaseRecommendationRepository extends DatabaseRecommendatio
147147
/**
148148
* Check is recommendation exist in database
149149
* @param clientMetadata
150-
* @param name
150+
* @param names
151151
*/
152152
async isExist(
153153
clientMetadata: ClientMetadata,
154-
name: string,
155-
): Promise<boolean> {
154+
names: string[],
155+
): Promise<Map<string, boolean>> {
156156
const { databaseId } = clientMetadata;
157157
try {
158-
this.logger.debug(`Checking is recommendation ${name} exist`, clientMetadata);
159-
const recommendation = await this.repository.findOneBy({ databaseId, name });
158+
this.logger.debug('Checking if recommendations exist', clientMetadata, names);
160159

161-
this.logger.debug(`Succeed to check is recommendation ${name} exist'`, clientMetadata);
162-
return !!recommendation;
160+
const results = await Promise.all(names.map((name) => this.repository.findOneBy({ databaseId, name })));
161+
162+
return results.reduce((acc, result, idx) => ({
163+
...acc,
164+
[names[idx]]: !!result,
165+
}), {} as Map<string, boolean>);
163166
} catch (err) {
164-
this.logger.error(`Failed to check is recommendation ${name} exist'`, err, clientMetadata);
165-
return false;
167+
this.logger.error('Failed to check existence of recommendations', err, clientMetadata, names);
168+
return {} as Map<string, boolean>;
166169
}
167170
}
168171

redisinsight/api/src/modules/database/database-connection.service.spec.ts

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,15 @@ describe('DatabaseConnectionService', () => {
9090
it('should call recommendationService', async () => {
9191
expect(await service.connect(mockCommonClientMetadata)).toEqual(undefined);
9292

93-
expect(recommendationService.check).toHaveBeenCalledTimes(3);
93+
expect(recommendationService.checkMulti).toHaveBeenCalledTimes(1);
9494

95-
expect(recommendationService.check).toBeCalledWith(
96-
mockCommonClientMetadata,
97-
RECOMMENDATION_NAMES.REDIS_VERSION,
98-
mockRedisGeneralInfo,
99-
);
100-
expect(recommendationService.check).toBeCalledWith(
101-
mockCommonClientMetadata,
102-
RECOMMENDATION_NAMES.LUA_SCRIPT,
103-
mockRedisGeneralInfo,
104-
);
105-
expect(recommendationService.check).toBeCalledWith(
95+
expect(recommendationService.checkMulti).toBeCalledWith(
10696
mockCommonClientMetadata,
107-
RECOMMENDATION_NAMES.BIG_AMOUNT_OF_CONNECTED_CLIENTS,
97+
[
98+
RECOMMENDATION_NAMES.REDIS_VERSION,
99+
RECOMMENDATION_NAMES.LUA_SCRIPT,
100+
RECOMMENDATION_NAMES.BIG_AMOUNT_OF_CONNECTED_CLIENTS,
101+
],
108102
mockRedisGeneralInfo,
109103
);
110104
});
@@ -116,21 +110,16 @@ describe('DatabaseConnectionService', () => {
116110

117111
expect(await service.connect(mockCommonClientMetadata)).toEqual(undefined);
118112

119-
expect(recommendationService.check).toHaveBeenCalledTimes(4);
113+
expect(recommendationService.check).toHaveBeenCalledTimes(1);
114+
expect(recommendationService.checkMulti).toHaveBeenCalledTimes(1);
120115

121-
expect(recommendationService.check).toBeCalledWith(
122-
mockCommonClientMetadata,
123-
RECOMMENDATION_NAMES.REDIS_VERSION,
124-
mockRedisGeneralInfo,
125-
);
126-
expect(recommendationService.check).toBeCalledWith(
127-
mockCommonClientMetadata,
128-
RECOMMENDATION_NAMES.LUA_SCRIPT,
129-
mockRedisGeneralInfo,
130-
);
131-
expect(recommendationService.check).toBeCalledWith(
116+
expect(recommendationService.checkMulti).toBeCalledWith(
132117
mockCommonClientMetadata,
133-
RECOMMENDATION_NAMES.BIG_AMOUNT_OF_CONNECTED_CLIENTS,
118+
[
119+
RECOMMENDATION_NAMES.REDIS_VERSION,
120+
RECOMMENDATION_NAMES.LUA_SCRIPT,
121+
RECOMMENDATION_NAMES.BIG_AMOUNT_OF_CONNECTED_CLIENTS,
122+
],
134123
mockRedisGeneralInfo,
135124
);
136125

redisinsight/api/src/modules/database/database-connection.service.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,13 @@ export class DatabaseConnectionService {
5454

5555
const generalInfo = await this.databaseInfoProvider.getRedisGeneralInfo(client);
5656

57-
this.recommendationService.check(
57+
this.recommendationService.checkMulti(
5858
clientMetadata,
59-
RECOMMENDATION_NAMES.REDIS_VERSION,
60-
generalInfo,
61-
);
62-
this.recommendationService.check(
63-
clientMetadata,
64-
RECOMMENDATION_NAMES.LUA_SCRIPT,
65-
generalInfo,
66-
);
67-
this.recommendationService.check(
68-
clientMetadata,
69-
RECOMMENDATION_NAMES.BIG_AMOUNT_OF_CONNECTED_CLIENTS,
59+
[
60+
RECOMMENDATION_NAMES.REDIS_VERSION,
61+
RECOMMENDATION_NAMES.LUA_SCRIPT,
62+
RECOMMENDATION_NAMES.BIG_AMOUNT_OF_CONNECTED_CLIENTS,
63+
],
7064
generalInfo,
7165
);
7266

0 commit comments

Comments
 (0)