Skip to content

Commit 2752012

Browse files
authored
feat: implement database connection watcher (#1270)
1 parent 74843e6 commit 2752012

File tree

2 files changed

+69
-6
lines changed

2 files changed

+69
-6
lines changed

.changeset/tricky-socks-invite.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"fuels-wallet": patch
3+
---
4+
5+
fix: database not recovering from error/closed state

packages/app/src/systems/Core/utils/database.ts

+64-6
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import type {
99
NetworkData,
1010
Vault,
1111
} from '@fuel-wallet/types';
12-
import type { Table } from 'dexie';
12+
import type { DbEvents, PromiseExtended, Table } from 'dexie';
1313
import Dexie from 'dexie';
1414
import 'dexie-observable';
1515
import { DATABASE_VERSION, VITE_FUEL_PROVIDER_URL } from '~/config';
1616
import type { Transaction } from '~/systems/Transaction/types';
1717

18+
type FailureEvents = Extract<keyof DbEvents, 'close' | 'blocked'>;
19+
1820
export class FuelDB extends Dexie {
1921
vaults!: Table<Vault, string>;
2022
accounts!: Table<Account, string>;
@@ -24,6 +26,8 @@ export class FuelDB extends Dexie {
2426
assets!: Table<AssetData, string>;
2527
abis!: Table<AbiTable, string>;
2628
errors!: Table<FuelWalletError, string>;
29+
integrityCheckInterval?: NodeJS.Timeout;
30+
restartAttempts = 0;
2731
readonly alwaysOpen = true;
2832

2933
constructor() {
@@ -51,16 +55,70 @@ export class FuelDB extends Dexie {
5155
id: createUUID(),
5256
});
5357
});
54-
this.on('blocked', () => this.restart('closed'));
55-
this.on('close', () => this.restart('blocked'));
58+
this.on('blocked', () => this.restart('blocked'));
59+
this.on('close', () => this.restart('close'));
60+
this.on('message', (e) => {
61+
console.log('fsk changed', e);
62+
});
63+
}
64+
65+
open() {
66+
return this.safeOpen().finally(() =>
67+
this.watchConnection()
68+
) as PromiseExtended<Dexie>;
69+
}
70+
71+
close(safeClose = false) {
72+
if (safeClose) {
73+
this.restartAttempts = 0;
74+
clearInterval(this.integrityCheckInterval);
75+
}
76+
return super.close();
77+
}
78+
79+
async safeOpen() {
80+
try {
81+
const result = await super.open();
82+
this.restartAttempts = 0;
83+
return result;
84+
} catch (err) {
85+
console.error('Failed to restart DB. Sending signal for restart');
86+
this.restart('blocked');
87+
throw err;
88+
}
89+
}
90+
91+
async ensureDatabaseOpen() {
92+
if (this.isOpen() && !this.hasBeenClosed() && !this.hasFailed()) return;
93+
94+
if (this.restartAttempts > 3) {
95+
console.error('Reached max attempts to open DB. Sending restart signal.');
96+
this.restart('blocked');
97+
return;
98+
}
99+
100+
this.restartAttempts += 1;
101+
console.warn('DB is not open. Attempting restart.');
102+
await this.safeOpen();
103+
}
104+
105+
watchConnection() {
106+
if (!this.alwaysOpen) return;
107+
108+
clearInterval(this.integrityCheckInterval);
109+
this.integrityCheckInterval = setInterval(() => {
110+
this.ensureDatabaseOpen();
111+
}, 1000);
56112
}
57113

58-
async restart(eventName: 'blocked' | 'closed') {
114+
async restart(eventName: FailureEvents) {
59115
if (!this.alwaysOpen) {
60116
return;
61117
}
62-
if (eventName !== 'closed') {
63-
this.close();
118+
if (eventName === 'close') {
119+
clearInterval(this.integrityCheckInterval);
120+
} else {
121+
this.close(true);
64122
}
65123

66124
this.open();

0 commit comments

Comments
 (0)