Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
7376ce5
feat(otlp-exporter-base): add retries to sendWithHttp
svetlanabrennan Aug 4, 2022
43320ee
feat(otlp-exporter-base): add tests and update abort logic
svetlanabrennan Aug 19, 2022
21771d5
feat(otlp-exporter-base): fix lint
svetlanabrennan Aug 26, 2022
b5fcd3d
Merge branch 'main' into add-retries-to-exporters
svetlanabrennan Aug 30, 2022
c9e54b0
feat(otlp-exporter-base): add retry test
svetlanabrennan Sep 22, 2022
75a50ad
feat(otlp-exporter-base): add retry to browser exporter and add tests
svetlanabrennan Oct 6, 2022
5bb910b
feat(otlp-exporter-base): refactor
svetlanabrennan Oct 6, 2022
bdff2b0
Merge branch 'main' into add-retries-to-exporters
svetlanabrennan Oct 6, 2022
213d228
feat(otlp-exporter-base): add jitter
svetlanabrennan Oct 20, 2022
5987db3
feat(otlp-exporter-base): initialize reqIsDestroyed to false
svetlanabrennan Oct 20, 2022
917e5cc
feat(otlp-exporter-base): add throttle logic
svetlanabrennan Oct 21, 2022
7e1d0c8
feat(otlp-exporter-base): add retry to readme
svetlanabrennan Oct 21, 2022
b2deb31
Merge branch 'add-retries-to-exporters' of github.com:svetlanabrennan…
svetlanabrennan Oct 21, 2022
24fa721
Merge branch 'main' into add-retries-to-exporters
svetlanabrennan Oct 21, 2022
7e45c91
feat(otlp-exporter-base): add changelog
svetlanabrennan Oct 21, 2022
e327d18
feat(otlp-exporter-base): update throttle time function
svetlanabrennan Oct 24, 2022
e3014b1
Merge branch 'main' into add-retries-to-exporters
svetlanabrennan Oct 24, 2022
3855c0f
feat(otlp-exporter-base): refactor sec difference in throttle fun
svetlanabrennan Oct 24, 2022
cf833aa
Merge branch 'add-retries-to-exporters' of github.com:svetlanabrennan…
svetlanabrennan Oct 24, 2022
4a658e7
feat(otlp-exporter-base): fix lint
svetlanabrennan Oct 24, 2022
ee0d214
Merge branch 'main' into add-retries-to-exporters
svetlanabrennan Jan 13, 2023
549e6f6
feat(otlp-exporter-base): fix lint
svetlanabrennan Jan 13, 2023
e9b6981
feat(otlp-exporter-base): fix lint
svetlanabrennan Jan 13, 2023
2b05f20
Merge remote-tracking branch 'upstream/main' into add-retries-to-expo…
svetlanabrennan Jan 13, 2023
f6e151f
Merge branch 'main' into add-retries-to-exporters
svetlanabrennan Jan 31, 2023
396b556
Merge branch 'main' into add-retries-to-exporters
svetlanabrennan Feb 8, 2023
9045027
feat(otlp-exporter-base): refactor retrieve throttle time func
svetlanabrennan Feb 16, 2023
e5547ee
Merge branch 'add-retries-to-exporters' of github.com:svetlanabrennan…
svetlanabrennan Feb 16, 2023
1d561fc
Merge branch 'main' into add-retries-to-exporters
svetlanabrennan Feb 16, 2023
ea268a3
feat(otlp-exporter-base): fix lint
svetlanabrennan Feb 16, 2023
f1a2b3d
Merge branch 'add-retries-to-exporters' of github.com:svetlanabrennan…
svetlanabrennan Feb 16, 2023
1074c2a
feat(otlp-exporter-base): move parseRetryAfterToMills to utils file
svetlanabrennan Feb 22, 2023
bab22bf
Merge branch 'main' into add-retries-to-exporters
svetlanabrennan Feb 22, 2023
02572c4
Merge branch 'main' into add-retries-to-exporters
legendecas Feb 28, 2023
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
Expand Up @@ -599,3 +599,50 @@ describe('when configuring via environment', () => {
envSource.OTEL_EXPORTER_OTLP_HEADERS = '';
});
});

describe('export with retry - real http request destroyed', () => {
let server: any;
let collectorTraceExporter: OTLPTraceExporter;
let collectorExporterConfig: OTLPExporterConfigBase;
let spans: ReadableSpan[];

beforeEach(() => {
server = sinon.fakeServer.create({
autoRespond: true
});
collectorExporterConfig = {
timeoutMillis: 1500
};
});

afterEach(() => {
server.restore();
});

describe('when "sendBeacon" is NOT available', () => {
beforeEach(() => {
(window.navigator as any).sendBeacon = false;
collectorTraceExporter = new OTLPTraceExporter(collectorExporterConfig);
});

it('should log the timeout request error message', done => {
spans = [];
spans.push(Object.assign({}, mockedReadableSpan));

let retry = 0;
server.respondWith('http://localhost:4318/v1/traces', function (xhr: any, id: any) {
retry++;
xhr.respond(502);
});

collectorTraceExporter.export(spans, result => {
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(error.message, 'Request Timeout');
assert.strictEqual(retry, 2);
done();
});
}).timeout(3000);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -554,37 +554,41 @@ describe('export - real http request destroyed before response received', () =>
});
});

describe('export - real http request destroyed after response received', () => {
describe('export with retry - real http request destroyed after response received', () => {
let collectorExporter: OTLPTraceExporter;
let collectorExporterConfig: OTLPExporterNodeConfigBase;
let spans: ReadableSpan[];
let retries = 0;

const server = http.createServer((_, res) => {
res.write('writing something');
retries++;
if (retries >= 2) {
res.statusCode = 200;
} else {
res.statusCode = 502;
}
res.end();
});
before(done => {
server.listen(8081, done);
});
after(done => {
after(function (done) {
server.close(done);
});
it('should log the timeout request error message', done => {
collectorExporterConfig = {
url: 'http://localhost:8081',
timeoutMillis: 300,
timeoutMillis: 1500,
};
collectorExporter = new OTLPTraceExporter(collectorExporterConfig);
spans = [];
spans.push(Object.assign({}, mockedReadableSpan));

setTimeout(() => {
collectorExporter.export(spans, result => {
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(error.message, 'Request Timeout');
done();
});
}, 0);
});
collectorExporter.export(spans, result => {
assert.strictEqual(result.code, core.ExportResultCode.SUCCESS);
assert.strictEqual(retries, 2);
done();
});
}).timeout(3000);
});

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
*/
import { diag } from '@opentelemetry/api';
import { OTLPExporterError } from '../../types';
import {
DEFAULT_EXPORT_MAX_ATTEMPTS,
DEFAULT_EXPORT_INITIAL_BACKOFF,
DEFAULT_EXPORT_BACKOFF_MULTIPLIER,
isExportRetryable
} from '../../util';

/**
* Send metrics/spans using browser navigator.sendBeacon
Expand Down Expand Up @@ -60,48 +66,86 @@ export function sendWithXhr(
onError: (error: OTLPExporterError) => void
): void {
let reqIsDestroyed: boolean;
let retryTimer: ReturnType<typeof setTimeout>;
let xhr: XMLHttpRequest;

const exporterTimer = setTimeout(() => {
clearTimeout(retryTimer);
reqIsDestroyed = true;
xhr.abort();

if (xhr.readyState === XMLHttpRequest.DONE) {
const err = new OTLPExporterError(
'Request Timeout'
);
onError(err);
} else {
xhr.abort();
}
}, exporterTimeout);

const xhr = new XMLHttpRequest();
xhr.open('POST', url);
const sendWithRetry = (retries = DEFAULT_EXPORT_MAX_ATTEMPTS, backoffMillis = DEFAULT_EXPORT_INITIAL_BACKOFF) => {
xhr = new XMLHttpRequest();
xhr.open('POST', url);

const defaultHeaders = {
'Accept': 'application/json',
'Content-Type': 'application/json',
};
const defaultHeaders = {
'Accept': 'application/json',
'Content-Type': 'application/json',
};

Object.entries({
...defaultHeaders,
...headers,
}).forEach(([k, v]) => {
xhr.setRequestHeader(k, v);
});
Object.entries({
...defaultHeaders,
...headers,
}).forEach(([k, v]) => {
xhr.setRequestHeader(k, v);
});

xhr.send(body);
xhr.send(body);

xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status >= 200 && xhr.status <= 299) {
clearTimeout(exporterTimer);
diag.debug('xhr success', body);
onSuccess();
} else if (reqIsDestroyed) {
const error = new OTLPExporterError(
'Request Timeout', xhr.status
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE && reqIsDestroyed === undefined) {
if (xhr.status >= 200 && xhr.status <= 299) {
diag.debug('xhr success', body);
onSuccess();
clearTimeout(exporterTimer);
clearTimeout(retryTimer);
} else if (xhr.status && isExportRetryable(xhr.status) && retries > 0) {
retryTimer = setTimeout(() => {
sendWithRetry(retries - 1, backoffMillis * DEFAULT_EXPORT_BACKOFF_MULTIPLIER);
}, backoffMillis);
} else {
const error = new OTLPExporterError(
`Failed to export with XHR (status: ${xhr.status})`,
xhr.status
);
onError(error);
clearTimeout(exporterTimer);
clearTimeout(retryTimer);
}
}
};

xhr.onabort = () => {
if (reqIsDestroyed) {
const err = new OTLPExporterError(
'Request Timeout'
);
onError(error);
} else {
const error = new OTLPExporterError(
`Failed to export with XHR (status: ${xhr.status})`,
xhr.status
onError(err);
}
clearTimeout(exporterTimer);
clearTimeout(retryTimer);
};

xhr.onerror = () => {
if (reqIsDestroyed) {
const err = new OTLPExporterError(
'Request Timeout'
);
clearTimeout(exporterTimer);
onError(error);
onError(err);
}
}
clearTimeout(exporterTimer);
clearTimeout(retryTimer);
};
};

sendWithRetry();
}
Loading