Skip to content

Commit

Permalink
Avoid ECONNRESET errors on idle timeout (#162947)
Browse files Browse the repository at this point in the history
### Summary

Address #82002 and
#75440

I think I found a breakthrough for this flaky behavior. I run the
integration test 800x locally, with different settings:

Adjusting both the delayed emission (send 1 char at a time), and the
socket idle timeout to have exacly the same value (e.g. `10 millis`), I
managed to get the `ECONNRESET` 100% of the times.

Thus, IIUC the ECONNRESET happens when the client tries to send a
character over the socket and at the same time the server responds with
the idle timeout.

Adjusting the values so that the delay between character emissions is
significantly larger than the idle timeout, e.g. 20 vs 5, I get `socket
hang up` 100% of the times.

Flaky Test Runner Pipeline - 300x 🟢
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4030
  • Loading branch information
gsoldevila authored Nov 22, 2023
1 parent 0e904b8 commit 721d68a
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 42 deletions.
4 changes: 2 additions & 2 deletions src/core/server/integration_tests/http/router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ describe('Options', () => {
});

describe('idleSocket', () => {
it.skip('should timeout if payload sending has too long of an idle period', async () => {
it('should timeout if payload sending has too long of an idle period', async () => {
const { server: innerServer, createRouter } = await server.setup(setupDeps);
const router = createRouter('/');

Expand All @@ -420,7 +420,7 @@ describe('Options', () => {
body: {
accepts: ['application/json'],
},
timeout: { idleSocket: 10 },
timeout: { idleSocket: 5 },
},
},
async (context, req, res) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class CorePluginRouteTimeoutsPlugin implements Plugin {
body: {
accepts: ['application/json'],
},
timeout: { idleSocket: 10 },
timeout: { idleSocket: 5 },
},
path: '/short_idle_socket_timeout',
validate: {
Expand Down
56 changes: 17 additions & 39 deletions test/plugin_functional/test_suites/core/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
*/

import expect from '@kbn/expect';
import { Test } from 'supertest';
import { PluginFunctionalProviderContext } from '../../services';
import type { Test } from 'supertest';
import type { PluginFunctionalProviderContext } from '../../services';

export default function ({ getService }: PluginFunctionalProviderContext) {
const supertest = getService('supertest');

// FLAKY: https://github.com/elastic/kibana/issues/75440
describe.skip('route', function () {
describe('route', function () {
describe('timeouts', function () {
const writeBodyCharAtATime = (request: Test, body: string, interval: number) => {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -45,7 +44,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
.set('Transfer-Encoding', 'chunked')
.set('kbn-xsrf', 'true');

const result = writeBodyCharAtATime(request, '{"foo":"bar"}', 10);
const result = writeBodyCharAtATime(request, '{"foo":"bar"}', 20);

await result.then(
(res) => {
Expand All @@ -65,7 +64,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
.set('Transfer-Encoding', 'chunked')
.set('kbn-xsrf', 'true');

const result = writeBodyCharAtATime(request, '{"foo":"bar"}', 10);
const result = writeBodyCharAtATime(request, '{"foo":"bar"}', 20);

await result.then(
(res) => {
Expand Down Expand Up @@ -107,7 +106,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
.set('Transfer-Encoding', 'chunked')
.set('kbn-xsrf', 'true');

const result = writeBodyCharAtATime(request, '{"responseDelay":0}', 10);
const result = writeBodyCharAtATime(request, '{"responseDelay":0}', 20);

await result.then(
(res) => {
Expand All @@ -119,44 +118,23 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
);
});

it('should timeout if servers response is too slow', async function () {
// start the request
const request = supertest
it('should timeout if servers response is too slow', async () => {
await supertest
.post('/short_idle_socket_timeout')
.send({ responseDelay: 100 })
.set('Content-Type', 'application/json')
.set('Transfer-Encoding', 'chunked')
.set('kbn-xsrf', 'true');

const result = writeBodyCharAtATime(request, '{"responseDelay":100}', 0);

await result.then(
(res) => {
expect(res).to.be(undefined);
},
(err) => {
expect(err.message).to.be('socket hang up');
}
);
.set('kbn-xsrf', 'true')
.then(() => expect('to throw').to.be('but it did NOT'))
.catch((error) => expect(error.message).to.be('socket hang up'));
});

it('should not timeout if servers response is fast enough', async function () {
// start the request
const request = supertest
it('should not timeout if servers response is fast enough', async () => {
await supertest
.post('/longer_idle_socket_timeout')
.send({ responseDelay: 100 })
.set('Content-Type', 'application/json')
.set('Transfer-Encoding', 'chunked')
.set('kbn-xsrf', 'true');

const result = writeBodyCharAtATime(request, '{"responseDelay":100}', 0);

await result.then(
(res) => {
expect(res).to.have.property('statusCode', 200);
},
(err) => {
expect(err).to.be(undefined);
}
);
.set('kbn-xsrf', 'true')
.expect(200);
});
});
});
Expand Down

0 comments on commit 721d68a

Please sign in to comment.