From 8d803b67c74b6386c3c740f05c6ac3693341b0e5 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 2 Aug 2021 16:30:21 -0400 Subject: [PATCH] LP1938729 Cache "cascade" setting values Cache values retrieved from 'open-ils.actor.settings.retrieve' lookups in IndexedDB, similar to to org unit setting lookups, to reduce the need for so many API calls for settings retrieval. Signed-off-by: Bill Erickson Signed-off-by: Jason Boyer --- .../eg2/src/app/core/server-store.service.ts | 111 ++++++++++++++---- .../src/eg2/src/app/staff/login.component.ts | 3 +- 2 files changed, 89 insertions(+), 25 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/core/server-store.service.ts b/Open-ILS/src/eg2/src/app/core/server-store.service.ts index b54a4c9a87..05dcbe12ed 100644 --- a/Open-ILS/src/eg2/src/app/core/server-store.service.ts +++ b/Open-ILS/src/eg2/src/app/core/server-store.service.ts @@ -2,8 +2,10 @@ * Set and get server-stored settings. */ import {Injectable} from '@angular/core'; +import {tap} from 'rxjs/operators'; import {AuthService} from './auth.service'; import {NetService} from './net.service'; +import {DbStoreService} from './db-store.service'; // Settings summary objects returned by the API interface ServerSettingSummary { @@ -17,9 +19,10 @@ interface ServerSettingSummary { @Injectable({providedIn: 'root'}) export class ServerStoreService { - cache: {[key: string]: ServerSettingSummary}; + cache: {[key: string]: any}; constructor( + private db: DbStoreService, private net: NetService, private auth: AuthService) { this.cache = {}; @@ -41,15 +44,15 @@ export class ServerStoreService { .toPromise().then(appliedCount => { - if (Number(appliedCount) > 0) { // value applied - return this.cache[key] = value; + if (Number(appliedCount) <= 0) { // no value applied + return Promise.reject( + `No user or workstation setting type exists for: "${key}".\n` + + 'Create a ws/user setting type or use setLocalItem() to ' + + 'store the value locally.' + ); } - return Promise.reject( - `No user or workstation setting type exists for: "${key}".\n` + - 'Create a ws/user setting type or use setLocalItem() to ' + - 'store the value locally.' - ); + return this.addSettingsToDb(setting); }); } @@ -77,9 +80,9 @@ export class ServerStoreService { } // Returns a set of key/value pairs for the requested settings - getItemBatch(keys: string[]): Promise { + getItemBatch(keys: string[]): Promise<{[key: string]: any}> { - const values: any = {}; + let values: any = {}; keys.forEach(key => { if (key in this.cache) { values[key] = this.cache[key]; @@ -98,33 +101,93 @@ export class ServerStoreService { return Promise.resolve({}); } - // Server call required. Limit the settings to lookup to those - // we don't already have cached. - const serverKeys = []; + // IndexedDB call required. + const dbKeys = []; + keys.forEach(key => { if (!Object.keys(values).includes(key)) { - serverKeys.push(key); + dbKeys.push(key); } }); - return new Promise((resolve, reject) => { - this.net.request( + return this.getSettingsFromDb(dbKeys) // Also appends to local cache. + .then(dbValues => values = Object.assign(values, dbValues)) + .then(_ => { + + const serverKeys = []; + keys.forEach(key => { + if (!Object.keys(values).includes(key)) { + serverKeys.push(key); + } + }); + + if (serverKeys.length === 0) { return values; } + + return this.net.request( 'open-ils.actor', 'open-ils.actor.settings.retrieve', serverKeys, this.auth.token() - ).subscribe( - summary => { - this.cache[summary.name] = - values[summary.name] = summary.value; - }, - err => reject, - () => resolve(values) - ); + + ).pipe(tap((summary: ServerSettingSummary) => { + this.cache[summary.name] = + values[summary.name] = summary.value; + + })).toPromise().then(__ => { + + const dbSets: any = {}; + serverKeys.forEach(sKey => dbSets[sKey] = values[sKey]); + + return this.addSettingsToDb(dbSets); + + }); }); } removeItem(key: string): Promise { return this.setItem(key, null); } + + private addSettingsToDb(values: {[key: string]: any}): Promise<{[key: string]: any}> { + + const rows = []; + Object.keys(values).forEach(name => { + // Anything added to the db should also be cached locally. + this.cache[name] = values[name]; + rows.push({name: name, value: JSON.stringify(values[name])}); + }); + + if (rows.length === 0) { return Promise.resolve(values); } + + return this.db.request({ + schema: 'cache', + table: 'Setting', + action: 'insertOrReplace', + rows: rows + }).then(_ => values).catch(_ => values); + } + + getSettingsFromDb(names: string[]): Promise<{[key: string]: any}> { + if (names.length === 0) { return Promise.resolve({}); } + + const values: any = {}; + + return this.db.request({ + schema: 'cache', + table: 'Setting', + action: 'selectWhereIn', + field: 'name', + value: names + }).then(settings => { + + // array of key => JSON-string objects + settings.forEach(setting => { + const value = JSON.parse(setting.value); + // propagate to local cache as well + values[setting.name] = this.cache[setting.name] = value; + }); + + return values; + }).catch(_ => values); + } } diff --git a/Open-ILS/src/eg2/src/app/staff/login.component.ts b/Open-ILS/src/eg2/src/app/staff/login.component.ts index 207c525bdf..d9b9621d04 100644 --- a/Open-ILS/src/eg2/src/app/staff/login.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/login.component.ts @@ -88,7 +88,8 @@ export class StaffLoginComponent implements OnInit { } else { - // Initial login clears cached org unit settings. + // Initial login clears cached org unit setting values + // and user/workstation setting values this.org.clearCachedSettings().then(_ => { // Force reload of the app after a successful login. -- 2.43.2