Skip to content

Commit 091dbe4

Browse files
authored
Merge pull request #2250 from murgatroid99/outlier_detection_tests
grpc-js: Add more outlier detection tests and tracing
2 parents aaa568f + 24c4cd7 commit 091dbe4

File tree

2 files changed

+172
-1
lines changed

2 files changed

+172
-1
lines changed

packages/grpc-js/src/load-balancer-outlier-detection.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,9 +423,10 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
423423
const targetRequestVolume = successRateConfig.request_volume;
424424
let addresesWithTargetVolume = 0;
425425
const successRates: number[] = []
426-
for (const mapEntry of this.addressMap.values()) {
426+
for (const [address, mapEntry] of this.addressMap) {
427427
const successes = mapEntry.counter.getLastSuccesses();
428428
const failures = mapEntry.counter.getLastFailures();
429+
trace('Stats for ' + address + ': successes=' + successes + ' failures=' + failures + ' targetRequestVolume=' + targetRequestVolume);
429430
if (successes + failures >= targetRequestVolume) {
430431
addresesWithTargetVolume += 1;
431432
successRates.push(successes/(successes + failures));
@@ -545,6 +546,7 @@ export class OutlierDetectionLoadBalancer implements LoadBalancer {
545546

546547
private startTimer(delayMs: number) {
547548
this.ejectionTimer = setTimeout(() => this.runChecks(), delayMs);
549+
this.ejectionTimer.unref?.();
548550
}
549551

550552
private runChecks() {

packages/grpc-js/test/test-outlier-detection.ts

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import * as path from 'path';
2020
import * as grpc from '../src';
2121
import { loadProtoFile } from './common';
2222
import { OutlierDetectionLoadBalancingConfig } from '../src/load-balancer-outlier-detection'
23+
import { ServiceClient } from '../src/make-client';
2324

2425
function multiDone(done: Mocha.Done, target: number) {
2526
let count = 0;
@@ -49,6 +50,54 @@ const defaultOutlierDetectionServiceConfig = {
4950

5051
const defaultOutlierDetectionServiceConfigString = JSON.stringify(defaultOutlierDetectionServiceConfig);
5152

53+
const successRateOutlierDetectionServiceConfig = {
54+
methodConfig: [],
55+
loadBalancingConfig: [
56+
{
57+
outlier_detection: {
58+
interval: {
59+
seconds: 1,
60+
nanos: 0
61+
},
62+
base_ejection_time: {
63+
seconds: 3,
64+
nanos: 0
65+
},
66+
success_rate_ejection: {
67+
request_volume: 5
68+
},
69+
child_policy: [{round_robin: {}}]
70+
}
71+
}
72+
]
73+
};
74+
75+
const successRateOutlierDetectionServiceConfigString = JSON.stringify(successRateOutlierDetectionServiceConfig);
76+
77+
const failurePercentageOutlierDetectionServiceConfig = {
78+
methodConfig: [],
79+
loadBalancingConfig: [
80+
{
81+
outlier_detection: {
82+
interval: {
83+
seconds: 1,
84+
nanos: 0
85+
},
86+
base_ejection_time: {
87+
seconds: 3,
88+
nanos: 0
89+
},
90+
failure_percentage_ejection: {
91+
request_volume: 5
92+
},
93+
child_policy: [{round_robin: {}}]
94+
}
95+
}
96+
]
97+
};
98+
99+
const falurePercentageOutlierDetectionServiceConfigString = JSON.stringify(failurePercentageOutlierDetectionServiceConfig);
100+
52101
const goodService = {
53102
echo: (call: grpc.ServerUnaryCall<any, any>, callback: grpc.sendUnaryData<any>) => {
54103
callback(null, call.request)
@@ -353,6 +402,20 @@ describe('Outlier detection', () => {
353402
badServer.forceShutdown();
354403
});
355404

405+
function makeManyRequests(makeOneRequest: (callback: (error?: Error) => void) => void, total: number, callback: (error?: Error) => void) {
406+
if (total === 0) {
407+
callback();
408+
return;
409+
}
410+
makeOneRequest(error => {
411+
if (error) {
412+
callback(error);
413+
return;
414+
}
415+
makeManyRequests(makeOneRequest, total - 1, callback);
416+
});
417+
}
418+
356419
it('Should allow normal operation with one server', done => {
357420
const client = new EchoService(`localhost:${goodPorts[0]}`, grpc.credentials.createInsecure(), {'grpc.service_config': defaultOutlierDetectionServiceConfigString});
358421
client.echo(
@@ -364,4 +427,110 @@ describe('Outlier detection', () => {
364427
}
365428
);
366429
});
430+
describe('Success rate', () => {
431+
let makeCheckedRequest: (callback: () => void) => void;
432+
let makeUncheckedRequest:(callback: (error?: Error) => void) => void;
433+
before(() => {
434+
const target = 'ipv4:///' + goodPorts.map(port => `127.0.0.1:${port}`).join(',') + `,127.0.0.1:${badPort}`;
435+
const client = new EchoService(target, grpc.credentials.createInsecure(), {'grpc.service_config': successRateOutlierDetectionServiceConfigString});
436+
makeUncheckedRequest = (callback: () => void) => {
437+
client.echo(
438+
{ value: 'test value', value2: 3 },
439+
(error: grpc.ServiceError, response: any) => {
440+
callback();
441+
}
442+
);
443+
};
444+
makeCheckedRequest = (callback: (error?: Error) => void) => {
445+
client.echo(
446+
{ value: 'test value', value2: 3 },
447+
(error: grpc.ServiceError, response: any) => {
448+
callback(error);
449+
}
450+
);
451+
};
452+
});
453+
it('Should eject a server if it is failing requests', done => {
454+
// Make a large volume of requests
455+
makeManyRequests(makeUncheckedRequest, 50, () => {
456+
// Give outlier detection time to run ejection checks
457+
setTimeout(() => {
458+
// Make enough requests to go around all servers
459+
makeManyRequests(makeCheckedRequest, 10, done);
460+
}, 1000);
461+
});
462+
});
463+
it('Should uneject a server after the ejection period', function(done) {
464+
this.timeout(5000);
465+
makeManyRequests(makeUncheckedRequest, 50, () => {
466+
setTimeout(() => {
467+
makeManyRequests(makeCheckedRequest, 10, error => {
468+
if (error) {
469+
done(error);
470+
return;
471+
}
472+
setTimeout(() => {
473+
makeManyRequests(makeCheckedRequest, 10, error => {
474+
assert(error);
475+
done();
476+
});
477+
}, 3000);
478+
});
479+
}, 1000);
480+
})
481+
});
482+
});
483+
describe('Failure percentage', () => {
484+
let makeCheckedRequest: (callback: () => void) => void;
485+
let makeUncheckedRequest:(callback: (error?: Error) => void) => void;
486+
before(() => {
487+
const target = 'ipv4:///' + goodPorts.map(port => `127.0.0.1:${port}`).join(',') + `,127.0.0.1:${badPort}`;
488+
const client = new EchoService(target, grpc.credentials.createInsecure(), {'grpc.service_config': falurePercentageOutlierDetectionServiceConfigString});
489+
makeUncheckedRequest = (callback: () => void) => {
490+
client.echo(
491+
{ value: 'test value', value2: 3 },
492+
(error: grpc.ServiceError, response: any) => {
493+
callback();
494+
}
495+
);
496+
};
497+
makeCheckedRequest = (callback: (error?: Error) => void) => {
498+
client.echo(
499+
{ value: 'test value', value2: 3 },
500+
(error: grpc.ServiceError, response: any) => {
501+
callback(error);
502+
}
503+
);
504+
};
505+
});
506+
it('Should eject a server if it is failing requests', done => {
507+
// Make a large volume of requests
508+
makeManyRequests(makeUncheckedRequest, 50, () => {
509+
// Give outlier detection time to run ejection checks
510+
setTimeout(() => {
511+
// Make enough requests to go around all servers
512+
makeManyRequests(makeCheckedRequest, 10, done);
513+
}, 1000);
514+
});
515+
});
516+
it('Should uneject a server after the ejection period', function(done) {
517+
this.timeout(5000);
518+
makeManyRequests(makeUncheckedRequest, 50, () => {
519+
setTimeout(() => {
520+
makeManyRequests(makeCheckedRequest, 10, error => {
521+
if (error) {
522+
done(error);
523+
return;
524+
}
525+
setTimeout(() => {
526+
makeManyRequests(makeCheckedRequest, 10, error => {
527+
assert(error);
528+
done();
529+
});
530+
}, 3000);
531+
});
532+
}, 1000);
533+
})
534+
});
535+
});
367536
});

0 commit comments

Comments
 (0)