Search code examples
javascriptangulartypescriptindexeddbangular2-observables

IndexedDB slow when repeating the same request


I'm implementing IndexedDB in AngularJS 2 using TypeScript and Observables. I found that opening the IndexedDB database and then fetching 500 records took less than a second. However, with that database connection still open, fetching the same data again took around 110 seconds. I've tried various options including cursors to retrieve the data all with the same results. There are only around 4000 records in this store too.

I've included some example code below to try and illustrate what I'm trying to do. Executing OfflineService.test() as the entry point.

export class OfflineService {
    constructor(private indexedDbService: IndexedDbService) {
    }

    test(): void {
        this.indexedDbService.openDb()
            .subscribe((open: boolean) => {
                if (!open) {
                    // IndexedDB not supported
                    return;
                }
                this.indexedDbService.getObjectsById("items", 1, 500)
                    .subscribe((results: any[]) => {
                        // results returned in < 1 second

                        // retrieve again
                        this.indexedDbService.getObjectsById("items", 1, 500)
                            .subscribe((results: any[]) => {
                                // results returned in around 110 seconds
                            });
                    });
            });
    }
}

export class IndexedDbService {
    private db: any;

    openDb(): Observable<boolean> {
        return Observable.create((observer: any) => {
            try {
                if (this.db) {
                    observer.next(true);
                    observer.complete();
                    return;
                }

                const request = indexedDB.open("testDb", 1);
                request.onupgradeneeded = (event: any) => {
                    event.currentTarget.result.createObjectStore("items", { keyPath: "Id", autoIncrement: false });
                };
                request.onsuccess = () => {
                    this.db = request.result;
                    observer.next(true);
                    observer.complete();
                };
                request.onerror = () => {
                    observer.next(false);
                    observer.complete();
                };
            } catch (err) {
                observer.next(false);
                observer.complete();
            }
        });
    }

    getObjectsById(storeName: string, lowerId: number, upperId: number): Observable<any> {
        return Observable.create((observer: any) => {
            try {
                const results: any[] = [];
                const store = this.getObjectStore(storeName, false);
                const request = store.openCursor(IDBKeyRange.bound(lowerId, upperId));
                request.onsuccess = () => {
                    var cursor = request.result;
                    if (cursor) {
                        results.push(cursor.value);
                        cursor.continue();
                    } else {
                        observer.next(results);
                        observer.complete();
                    }
                };
                request.onerror = (event) => {
                    observer.error(Error(event.target.errorCode));
                };
            } catch (err) {
                observer.error(err);
            }
        });
    }

    private getObjectStore(storeName: string, writable: boolean): any {
        const transaction = this.db.transaction(storeName, writable ? "readwrite" : "readonly");
        return transaction.objectStore(storeName);
    }
}

Solution

  • I finally figured out that it wasn't anything to do with IndexedDB or Observables. It was just that after loading the first set of results, the second call to the IndexedDB was slower simply because of more memory being used. Clearing the original results before making the second call fixed the performance problem.