Skip to content

Commit 028f47f

Browse files
author
Artem
committed
#RI-3844 rework workaround for blocked client due to ft.search command
1 parent 2866b5e commit 028f47f

File tree

6 files changed

+34
-44
lines changed

6 files changed

+34
-44
lines changed

redisinsight/api/config/default.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ export default {
5656
buildType: process.env.BUILD_TYPE || 'ELECTRON',
5757
appVersion: process.env.APP_VERSION || '2.0.0',
5858
requestTimeout: parseInt(process.env.REQUEST_TIMEOUT, 10) || 10000,
59-
ftSearchRequestTimeout: parseInt(process.env.REQUEST_TIMEOUT, 10) || 45_000,
6059
excludeRoutes: [],
6160
},
6261
sockets: {

redisinsight/api/config/test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ export default {
22
server: {
33
env: 'test',
44
requestTimeout: 1000,
5-
ftSearchRequestTimeout: 1000,
65
},
76
profiler: {
87
logFileIdleThreshold: parseInt(process.env.PROFILER_LOG_FILE_IDLE_THRESHOLD, 10) || 1000 * 2, // 3sec

redisinsight/api/src/constants/error-messages.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export default {
1111
WRONG_DATABASE_TYPE: 'Wrong database type.',
1212
CONNECTION_TIMEOUT:
1313
'The connection has timed out, please check the connection details.',
14-
FT_SEARCH_COMMAND_TIMED_OUT: 'Command timed out.',
1514
AUTHENTICATION_FAILED: () => 'Failed to authenticate, please check the username or password.',
1615
INCORRECT_DATABASE_URL: (url) => `Could not connect to ${url}, please check the connection details.`,
1716
INCORRECT_CERTIFICATES: (url) => `Could not connect to ${url}, please check the CA or Client certificate.`,

redisinsight/api/src/modules/browser/services/redisearch/redisearch.service.spec.ts

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing';
22
import {
33
ConflictException,
44
ForbiddenException,
5-
GatewayTimeoutException,
65
} from '@nestjs/common';
7-
import ERROR_MESSAGES from 'src/constants/error-messages';
86
import { when } from 'jest-when';
97
import {
108
mockDatabase,
@@ -242,7 +240,7 @@ describe('RedisearchService', () => {
242240
'LIMIT', `${mockSearchRedisearchDto.offset}`, `${mockSearchRedisearchDto.limit}`,
243241
],
244242
}));
245-
expect(nodeClient.sendCommand).toHaveBeenCalledWith(jasmine.objectContaining( {
243+
expect(nodeClient.sendCommand).toHaveBeenCalledWith(jasmine.objectContaining({
246244
name: 'FT.CONFIG',
247245
args: [
248246
'GET',
@@ -279,7 +277,7 @@ describe('RedisearchService', () => {
279277
'LIMIT', `${mockSearchRedisearchDto.offset}`, `${mockSearchRedisearchDto.limit}`,
280278
],
281279
}));
282-
expect(clusterClient.sendCommand).toHaveBeenCalledWith(jasmine.objectContaining( {
280+
expect(clusterClient.sendCommand).toHaveBeenCalledWith(jasmine.objectContaining({
283281
name: 'FT.CONFIG',
284282
args: [
285283
'GET',
@@ -299,20 +297,5 @@ describe('RedisearchService', () => {
299297
expect(e).toBeInstanceOf(ForbiddenException);
300298
}
301299
});
302-
it('should handle produce BadGateway issue due to no response from ft.search command', async () => {
303-
when(nodeClient.sendCommand)
304-
.calledWith(jasmine.objectContaining({ name: 'FT.SEARCH' }))
305-
.mockResolvedValue(new Promise((res) => {
306-
setTimeout(() => res([100, keyName1, keyName2]), 1100);
307-
}));
308-
309-
try {
310-
await service.search(mockClientOptions, mockSearchRedisearchDto);
311-
fail();
312-
} catch (e) {
313-
expect(e).toBeInstanceOf(GatewayTimeoutException);
314-
expect(e.message).toEqual(ERROR_MESSAGES.FT_SEARCH_COMMAND_TIMED_OUT);
315-
}
316-
});
317300
});
318301
});

redisinsight/api/src/modules/browser/services/redisearch/redisearch.service.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { toNumber, uniq } from 'lodash';
33
import {
44
BadRequestException,
55
ConflictException,
6-
GatewayTimeoutException,
76
HttpException,
87
Injectable,
98
Logger,
@@ -20,11 +19,8 @@ import { GetKeysWithDetailsResponse } from 'src/modules/browser/dto';
2019
import { RedisErrorCodes } from 'src/constants';
2120
import { plainToClass } from 'class-transformer';
2221
import { numberWithSpaces } from 'src/utils/base.helper';
23-
import config from 'src/utils/config';
2422
import { BrowserToolService } from '../browser-tool/browser-tool.service';
2523

26-
const serverConfig = config.get('server');
27-
2824
@Injectable()
2925
export class RedisearchService {
3026
private logger = new Logger('RedisearchService');
@@ -152,22 +148,6 @@ export class RedisearchService {
152148

153149
const client = await this.browserTool.getRedisClient(clientOptions);
154150

155-
// special workaround to avoid blocking client with ft.search command
156-
// due to RediSearch issue
157-
const [total, ...keyNames] = await Promise.race([
158-
client.sendCommand(
159-
new Command('FT.SEARCH', [index, query, 'NOCONTENT', 'LIMIT', offset, limit]),
160-
),
161-
new Promise((res, rej) => setTimeout(() => {
162-
try {
163-
client.disconnect();
164-
} catch (e) {
165-
// ignore any error related to disconnect client
166-
}
167-
rej(new GatewayTimeoutException(ERROR_MESSAGES.FT_SEARCH_COMMAND_TIMED_OUT));
168-
}, serverConfig.ftSearchRequestTimeout)),
169-
]);
170-
171151
try {
172152
const [[, maxSearchResults]] = await client.sendCommand(
173153
// response: [ [ 'MAXSEARCHRESULTS', '10000' ] ]
@@ -181,6 +161,16 @@ export class RedisearchService {
181161
maxResults = null;
182162
}
183163

164+
// Workaround: recalculate limit to not query more then MAXSEARCHRESULTS
165+
let safeLimit = limit;
166+
if (maxResults && offset + limit > maxResults) {
167+
safeLimit = offset <= maxResults ? maxResults - offset : limit;
168+
}
169+
170+
const [total, ...keyNames] = await client.sendCommand(
171+
new Command('FT.SEARCH', [index, query, 'NOCONTENT', 'LIMIT', offset, safeLimit]),
172+
);
173+
184174
return plainToClass(GetKeysWithDetailsResponse, {
185175
cursor: limit + offset,
186176
total,

redisinsight/api/test/api/redisearch/POST-databases-id-redisearch-search.test.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const mainCheckFn = getMainCheckFn(endpoint);
4545
describe('POST /databases/:id/redisearch/search', () => {
4646
requirements('!rte.bigData', 'rte.modules.search');
4747
before(async () => rte.data.generateRedisearchIndexes(true));
48+
beforeEach(() => rte.data.setRedisearchConfig('MAXSEARCHRESULTS', '10000'));
4849

4950
describe('Main', () => {
5051
describe('Validation', () => {
@@ -83,17 +84,36 @@ describe('POST /databases/:id/redisearch/search', () => {
8384
expect(body.maxResults).to.gte(10000);
8485
},
8586
},
87+
{
88+
name: 'Should modify limit to not exceed available search limitation',
89+
data: {
90+
...validInputData,
91+
offset: 0,
92+
limit: 10,
93+
},
94+
checkFn: async ({ body }) => {
95+
expect(body.keys.length).to.eq(1);
96+
expect(body.cursor).to.eq(10);
97+
expect(body.scanned).to.eq(1);
98+
expect(body.total).to.eq(2000);
99+
expect(body.maxResults).to.gte(1);
100+
},
101+
before: () => rte.data.setRedisearchConfig('MAXSEARCHRESULTS', '1'),
102+
},
86103
{
87104
name: 'Should return custom error message if MAXSEARCHRESULTS less than request.limit',
88-
data: validInputData,
105+
data: {
106+
...validInputData,
107+
offset: 10,
108+
limit: 10,
109+
},
89110
statusCode: 400,
90111
responseBody: {
91112
statusCode: 400,
92113
error: 'Bad Request',
93114
message: `Set MAXSEARCHRESULTS to at least ${numberWithSpaces(validInputData.limit)}.`,
94115
},
95116
before: () => rte.data.setRedisearchConfig('MAXSEARCHRESULTS', '1'),
96-
after: () => rte.data.setRedisearchConfig('MAXSEARCHRESULTS', '10000'),
97117
},
98118
].map(mainCheckFn);
99119
});

0 commit comments

Comments
 (0)