mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
feat(data-connect): Align SQL Connect Stream Transport Exponential Backoff Parameters with Other Platform SDKs (#10221)
* udpate exponential backoff params to match other SDKs * add changeset * update changeset * Apply reviewer suggestion * yarn run format --------- Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
committed by
GitHub
co-authored by
Hanzo Dev
parent
18d46d06dd
commit
6628ce177d
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@firebase/data-connect': patch
|
||||
---
|
||||
|
||||
Align SQL Connect Stream Transport exponential backoff parameters with other platform SDKs. No need for public release note.
|
||||
@@ -54,13 +54,9 @@ const IDLE_CONNECTION_TIMEOUT_MS = 15 * 1000; // 15 seconds
|
||||
/** Initial reconnect delay in ms */
|
||||
const INITIAL_RECONNECT_DELAY_MS = 1000; // 1 second
|
||||
/** Max reconnect delay in ms */
|
||||
const MAX_RECONNECT_DELAY_MS = 30 * 1000; // 30 seconds
|
||||
/** Max random jitter to add to reconnect delay in ms */
|
||||
const MAX_RECONNECT_JITTER_MS = 0.5 * 1000; // 0.5 seconds
|
||||
const MAX_RECONNECT_DELAY_MS = 60 * 1000; // 60 seconds
|
||||
/** Factor to multiply delay by on failure */
|
||||
const RECONNECT_BACKOFF_FACTOR = 1.3;
|
||||
/** Max number of reconnection attempts before giving up */
|
||||
const MAX_RECONNECT_ATTEMPTS = 10;
|
||||
const RECONNECT_BACKOFF_FACTOR = 1.5;
|
||||
|
||||
/**
|
||||
* A promise returned to the user when invoking an operation via {@linkcode AbstractDataConnectStreamTransport.invokeQuery | invokeQuery}
|
||||
@@ -445,19 +441,13 @@ export abstract class AbstractDataConnectStreamTransport extends AbstractDataCon
|
||||
if (this.reconnectTimer) {
|
||||
return;
|
||||
}
|
||||
if (this.reconnectAttempts++ >= MAX_RECONNECT_ATTEMPTS) {
|
||||
const errorString =
|
||||
'Stream disconnected and could not reconnect - max stream reconnection attempts reached.';
|
||||
logError(errorString);
|
||||
void this.cleanupAndTerminate(Code.OTHER, errorString);
|
||||
return;
|
||||
}
|
||||
this.reconnectAttempts++;
|
||||
const delay = this.reconnectDelayMs;
|
||||
this.reconnectDelayMs = Math.min(
|
||||
this.reconnectDelayMs * RECONNECT_BACKOFF_FACTOR,
|
||||
MAX_RECONNECT_DELAY_MS
|
||||
);
|
||||
const jitter = Math.random() * MAX_RECONNECT_JITTER_MS;
|
||||
const jitter = (Math.random() - 0.5) * delay;
|
||||
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.reconnectTimer = null;
|
||||
|
||||
@@ -22,7 +22,7 @@ import * as sinon from 'sinon';
|
||||
import sinonChai from 'sinon-chai';
|
||||
|
||||
import { DataConnectOptions } from '../../src/api/DataConnect';
|
||||
import { Code } from '../../src/core/error';
|
||||
import { Code, DataConnectError } from '../../src/core/error';
|
||||
import { AuthTokenProvider } from '../../src/core/FirebaseAuthProvider';
|
||||
import * as logger from '../../src/logger';
|
||||
import {
|
||||
@@ -1993,6 +1993,64 @@ describe('AbstractDataConnectStreamTransport', () => {
|
||||
delete (global as any).document;
|
||||
}
|
||||
});
|
||||
|
||||
it('should exponentially back off with 1.5 multiplier and [-0.5x, +0.5x] jitter capped at 60s', async () => {
|
||||
ensureConnectionStub.rejects(
|
||||
new DataConnectError(Code.OTHER, 'Connection failed')
|
||||
);
|
||||
const randomStub = sinon.stub(Math, 'random');
|
||||
const observer = {
|
||||
onData: sinon.spy(),
|
||||
onDisconnect: sinon.spy(),
|
||||
onError: sinon.spy()
|
||||
};
|
||||
transport.invokeSubscribe(observer, queryName1, variables1);
|
||||
|
||||
// Attempt 1: base delay 1000ms, random=0 -> jitter -500ms -> delay 500ms
|
||||
randomStub.returns(0);
|
||||
transport.onStreamClose(1006, 'Abnormal Closure');
|
||||
|
||||
await clock.tickAsync(499);
|
||||
expect(ensureConnectionStub).to.not.have.been.called;
|
||||
|
||||
// Set random=1 before attempt 1 failure triggers startReconnectBackoff for attempt 2
|
||||
// Attempt 2: base delay 1500ms, random=1 -> jitter +750ms -> delay 2250ms
|
||||
randomStub.returns(1);
|
||||
await clock.tickAsync(1);
|
||||
expect(ensureConnectionStub).to.have.been.calledOnce;
|
||||
|
||||
await clock.tickAsync(2249);
|
||||
expect(ensureConnectionStub).to.have.been.calledOnce;
|
||||
|
||||
// Set random=0.5 before attempt 2 failure triggers startReconnectBackoff for attempt 3
|
||||
// Attempt 3: base delay 2250ms, random=0.5 -> jitter 0ms -> delay 2250ms
|
||||
randomStub.returns(0.5);
|
||||
await clock.tickAsync(1);
|
||||
expect(ensureConnectionStub).to.have.been.calledTwice;
|
||||
|
||||
await clock.tickAsync(2250);
|
||||
expect(ensureConnectionStub).to.have.been.calledThrice;
|
||||
});
|
||||
|
||||
it('should attempt reconnection indefinitely without a max attempt limit', async () => {
|
||||
ensureConnectionStub.rejects(
|
||||
new DataConnectError(Code.OTHER, 'Connection failed')
|
||||
);
|
||||
const observer = {
|
||||
onData: sinon.spy(),
|
||||
onDisconnect: sinon.spy(),
|
||||
onError: sinon.spy()
|
||||
};
|
||||
transport.invokeSubscribe(observer, queryName1, variables1);
|
||||
transport.onStreamClose(1006, 'Abnormal Closure');
|
||||
|
||||
// Fast-forward past 15 reconnection attempts
|
||||
for (let i = 0; i < 15; i++) {
|
||||
await clock.tickAsync(65000);
|
||||
}
|
||||
|
||||
expect(ensureConnectionStub.callCount).to.be.at.least(15);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user