@@ -164,6 +164,8 @@ function deleteObject(db: IDBDatabase, key: string): Promise<void> {
164
164
165
165
/** @internal */
166
166
export const _POLLING_INTERVAL_MS = 800 ;
167
+ /** @internal */
168
+ export const _TRANSACTION_RETRY_COUNT = 3 ;
167
169
168
170
class IndexedDBLocalPersistence implements Persistence {
169
171
static type : 'LOCAL' = 'LOCAL' ;
@@ -193,14 +195,34 @@ class IndexedDBLocalPersistence implements Persistence {
193
195
) ;
194
196
}
195
197
196
- private async initialize ( ) : Promise < IDBDatabase > {
198
+ async _openDb ( ) : Promise < IDBDatabase > {
197
199
if ( this . db ) {
198
200
return this . db ;
199
201
}
200
202
this . db = await _openDatabase ( ) ;
201
203
return this . db ;
202
204
}
203
205
206
+ async _withRetries < T > ( op : ( db : IDBDatabase ) => Promise < T > ) : Promise < T > {
207
+ let numAttempts = 0 ;
208
+
209
+ while ( true ) {
210
+ try {
211
+ const db = await this . _openDb ( ) ;
212
+ return await op ( db ) ;
213
+ } catch ( e ) {
214
+ if ( numAttempts ++ > _TRANSACTION_RETRY_COUNT ) {
215
+ throw e ;
216
+ }
217
+ if ( this . db ) {
218
+ this . db . close ( ) ;
219
+ this . db = undefined ;
220
+ }
221
+ // TODO: consider adding exponential backoff
222
+ }
223
+ }
224
+ }
225
+
204
226
/**
205
227
* IndexedDB events do not propagate from the main window to the worker context. We rely on a
206
228
* postMessage interface to send these events to the worker ourselves.
@@ -318,38 +340,35 @@ class IndexedDBLocalPersistence implements Persistence {
318
340
}
319
341
320
342
async _set ( key : string , value : PersistenceValue ) : Promise < void > {
321
- const db = await this . initialize ( ) ;
322
343
return this . _withPendingWrite ( async ( ) => {
323
- await _putObject ( db , key , value ) ;
344
+ await this . _withRetries ( ( db : IDBDatabase ) => _putObject ( db , key , value ) ) ;
324
345
this . localCache [ key ] = value ;
325
346
return this . notifyServiceWorker ( key ) ;
326
347
} ) ;
327
348
}
328
349
329
350
async _get < T extends PersistenceValue > ( key : string ) : Promise < T | null > {
330
- const db = await this . initialize ( ) ;
331
- const obj = ( await getObject ( db , key ) ) as T ;
351
+ const obj = ( await this . _withRetries ( ( db : IDBDatabase ) =>
352
+ getObject ( db , key )
353
+ ) ) as T ;
332
354
this . localCache [ key ] = obj ;
333
355
return obj ;
334
356
}
335
357
336
358
async _remove ( key : string ) : Promise < void > {
337
- const db = await this . initialize ( ) ;
338
359
return this . _withPendingWrite ( async ( ) => {
339
- await deleteObject ( db , key ) ;
360
+ await this . _withRetries ( ( db : IDBDatabase ) => deleteObject ( db , key ) ) ;
340
361
delete this . localCache [ key ] ;
341
362
return this . notifyServiceWorker ( key ) ;
342
363
} ) ;
343
364
}
344
365
345
366
private async _poll ( ) : Promise < string [ ] > {
346
- const db = await _openDatabase ( ) ;
347
-
348
367
// TODO: check if we need to fallback if getAll is not supported
349
- const getAllRequest = getObjectStore ( db , false ) . getAll ( ) ;
350
- const result = await new DBPromise < DBObject [ ] | null > (
351
- getAllRequest
352
- ) . toPromise ( ) ;
368
+ const result = await this . _withRetries ( ( db : IDBDatabase ) => {
369
+ const getAllRequest = getObjectStore ( db , false ) . getAll ( ) ;
370
+ return new DBPromise < DBObject [ ] | null > ( getAllRequest ) . toPromise ( ) ;
371
+ } ) ;
353
372
354
373
if ( ! result ) {
355
374
return [ ] ;
0 commit comments