Exponentially increases retry time when extending session fails (#125973) (#126188)

* Exponentially increases retry time when extending session fails

* Ensure unit test fails without exponential retry backoff

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit c5918c7e07)

Co-authored-by: Thom Heymann <190132+thomheymann@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2022-02-22 16:14:34 -05:00 committed by GitHub
parent d5dd05605e
commit 557be1fc84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 3 deletions

View file

@ -130,9 +130,12 @@ describe('SessionTimeout', () => {
await sessionTimeout.start();
expect(http.fetch).toHaveBeenCalledTimes(1);
// Increment system time enough so that session extension gets triggered
// Increment system time far enough to bypass throttle time
nowMock.mockReturnValue(Date.now() + SESSION_EXTENSION_THROTTLE_MS + 10);
// Trigger session extension and wait for next tick
window.dispatchEvent(new Event('mousemove'));
await new Promise((resolve) => process.nextTick(resolve));
expect(http.fetch).toHaveBeenCalledTimes(2);
expect(http.fetch).toHaveBeenLastCalledWith(
@ -159,11 +162,44 @@ describe('SessionTimeout', () => {
expect(http.fetch).toHaveBeenCalledTimes(1);
// Trigger session extension and wait for next tick
window.dispatchEvent(new Event('mousemove'));
await new Promise((resolve) => process.nextTick(resolve));
expect(http.fetch).toHaveBeenCalledTimes(1);
});
it('exponentially increases retry time when extending session fails', async () => {
nowMock.mockReturnValue(0);
const { sessionTimeout, http } = createSessionTimeout();
await sessionTimeout.start();
expect(http.fetch).toHaveBeenCalledTimes(1);
// Increment system time far enough to bypass throttle time
nowMock.mockReturnValue(Date.now() + SESSION_EXTENSION_THROTTLE_MS + 10);
// Now make subsequent HTTP calls fail
http.fetch.mockRejectedValue(new Error('Failure'));
// Trigger session extension and wait for next tick
window.dispatchEvent(new Event('mousemove'));
await new Promise((resolve) => process.nextTick(resolve));
expect(http.fetch).toHaveBeenCalledTimes(2);
// Increment system time far enough to bypass throttle time
nowMock.mockReturnValue(Date.now() + SESSION_EXTENSION_THROTTLE_MS + 10);
// Trigger session extension and wait for next tick
window.dispatchEvent(new Event('mousemove'));
await new Promise((resolve) => process.nextTick(resolve));
// Without exponential retry backoff, this would have been called a 3rd time
expect(http.fetch).toHaveBeenCalledTimes(2);
});
it('marks HTTP requests as system requests when tab is not visible', async () => {
const { sessionTimeout, http } = createSessionTimeout();
await sessionTimeout.start();

View file

@ -38,6 +38,7 @@ export class SessionTimeout {
private isVisible = document.visibilityState !== 'hidden';
private isFetchingSessionInfo = false;
private consecutiveErrorCount = 0;
private snoozedWarningState?: SessionState;
private sessionState$ = new BehaviorSubject<SessionState>({
@ -218,7 +219,8 @@ export class SessionTimeout {
return (
!this.isFetchingSessionInfo &&
!this.warningToast &&
Date.now() > lastExtensionTime + SESSION_EXTENSION_THROTTLE_MS
Date.now() >
lastExtensionTime + SESSION_EXTENSION_THROTTLE_MS * Math.exp(this.consecutiveErrorCount)
);
}
@ -229,6 +231,7 @@ export class SessionTimeout {
method: extend ? 'POST' : 'GET',
asSystemRequest: !extend,
});
this.consecutiveErrorCount = 0;
if (sessionInfo) {
const { expiresInMs, canBeExtended } = sessionInfo;
const nextState: SessionState = {
@ -243,7 +246,7 @@ export class SessionTimeout {
return nextState;
}
} catch (error) {
// ignore
this.consecutiveErrorCount++;
} finally {
this.isFetchingSessionInfo = false;
}