mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-11 22:18:54 +08:00
feat(core): improve reconnect handling (#15458)
#### PR Dependency Tree * **PR #15458** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved realtime connection recovery with progressively longer retry delays, capped to prevent excessive waiting. * Connection retries now reset after successful reconnection and stop cleanly when no longer needed. * Reduced false error alerts by confirming repeated failures before notifying users. * Added cancellation handling when connection context changes or the service is closed. * **User Experience** * Realtime connection alerts now identify authentication, network, server, and timeout issues with localized messages. * Active alerts are automatically dismissed when the connection recovers. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -117,9 +117,12 @@ test('retry when connect failed', async () => {
|
||||
class TestConnection extends AutoReconnectConnection {
|
||||
override retryDelay = 300;
|
||||
connectCount = 0;
|
||||
retryDelayFor(retryCount: number) {
|
||||
return this.getRetryDelay(retryCount);
|
||||
}
|
||||
override async doConnect() {
|
||||
this.connectCount++;
|
||||
if (this.connectCount === 3) {
|
||||
if (this.connectCount >= 3) {
|
||||
return { hello: 'world' };
|
||||
}
|
||||
throw new Error('not connected, count: ' + this.connectCount);
|
||||
@@ -127,9 +130,15 @@ test('retry when connect failed', async () => {
|
||||
override doDisconnect() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
triggerError(error: Error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
|
||||
const connection = new TestConnection();
|
||||
expect([0, 1, 2, 8].map(count => connection.retryDelayFor(count))).toEqual([
|
||||
300, 600, 1200, 60000,
|
||||
]);
|
||||
connection.connect();
|
||||
|
||||
await vitest.waitFor(() => {
|
||||
@@ -149,6 +158,12 @@ test('retry when connect failed', async () => {
|
||||
expect(connection.status).toBe('connected');
|
||||
expect(connection.error).toBeUndefined();
|
||||
});
|
||||
|
||||
connection.triggerError(new Error('disconnected'));
|
||||
await vitest.waitFor(() => {
|
||||
expect(connection.connectCount).toBe(4);
|
||||
expect(connection.status).toBe('connected');
|
||||
});
|
||||
});
|
||||
|
||||
test('retry when error', async () => {
|
||||
|
||||
@@ -31,7 +31,9 @@ export abstract class AutoReconnectConnection<
|
||||
private _status: ConnectionStatus = 'idle';
|
||||
private _error: Error | undefined = undefined;
|
||||
retryDelay = 3000;
|
||||
maxRetryDelay = 60000;
|
||||
connectingTimeout = 15000;
|
||||
private retryCount = 0;
|
||||
private refCount = 0;
|
||||
private connectingAbort?: AbortController;
|
||||
private reconnectingAbort?: AbortController;
|
||||
@@ -103,6 +105,7 @@ export abstract class AutoReconnectConnection<
|
||||
clearTimeout(timeout);
|
||||
if (!signal.aborted) {
|
||||
this._inner = value;
|
||||
this.retryCount = 0;
|
||||
this.setStatus('connected');
|
||||
} else {
|
||||
try {
|
||||
@@ -150,16 +153,21 @@ export abstract class AutoReconnectConnection<
|
||||
|
||||
this.reconnectingAbort = new AbortController();
|
||||
const signal = this.reconnectingAbort.signal;
|
||||
const retryDelay = this.getRetryDelay(this.retryCount++);
|
||||
const timeout = setTimeout(() => {
|
||||
if (!signal.aborted) {
|
||||
this.innerConnect();
|
||||
}
|
||||
}, this.retryDelay);
|
||||
}, retryDelay);
|
||||
signal.addEventListener('abort', () => {
|
||||
clearTimeout(timeout);
|
||||
});
|
||||
}
|
||||
|
||||
protected getRetryDelay(retryCount: number) {
|
||||
return Math.min(this.retryDelay * 2 ** retryCount, this.maxRetryDelay);
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.refCount++;
|
||||
if (this.refCount === 1) {
|
||||
@@ -175,6 +183,7 @@ export abstract class AutoReconnectConnection<
|
||||
}
|
||||
if (this.refCount === 0) {
|
||||
this.innerDisconnect();
|
||||
this.retryCount = 0;
|
||||
this.setStatus('closed');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user