From 1b24f0955d137ee6032684ab59e0d19db7d049f2 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Thu, 17 Feb 2022 13:52:40 +0100 Subject: [PATCH] MOBILE-3821 core: Configure lazy cache lifetime --- .../classes/database/database-table-proxy.ts | 78 ++++++++++--------- src/core/classes/database/database-table.ts | 35 ++++++++- .../classes/database/debug-database-table.ts | 10 ++- .../classes/database/eager-database-table.ts | 2 + .../classes/database/lazy-database-table.ts | 52 ++++++++++++- src/core/classes/tests/database-table.test.ts | 8 +- src/core/services/sites.ts | 8 +- src/types/config.d.ts | 2 +- 8 files changed, 139 insertions(+), 56 deletions(-) diff --git a/src/core/classes/database/database-table-proxy.ts b/src/core/classes/database/database-table-proxy.ts index 7e183f95a..b1cba7e88 100644 --- a/src/core/classes/database/database-table-proxy.ts +++ b/src/core/classes/database/database-table-proxy.ts @@ -14,10 +14,11 @@ import { CoreConstants } from '@/core/constants'; import { asyncInstance } from '@/core/utils/async-instance'; -import { SQLiteDB, SQLiteDBRecordValues } from '@classes/sqlitedb'; +import { SQLiteDBRecordValues } from '@classes/sqlitedb'; import { CoreConfig, CoreConfigProvider } from '@services/config'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { + CoreDatabaseConfiguration, CoreDatabaseReducer, CoreDatabaseTable, CoreDatabaseConditions, @@ -40,7 +41,8 @@ export class CoreDatabaseTableProxy< PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey > extends CoreDatabaseTable { - protected config: CoreDatabaseConfiguration; + protected readonly DEFAULT_CACHING_STRATEGY = CoreDatabaseCachingStrategy.None; + protected target = asyncInstance>(); protected environmentObserver?: CoreEventObserver; protected targetConstructors: Record< @@ -52,21 +54,12 @@ export class CoreDatabaseTableProxy< [CoreDatabaseCachingStrategy.None]: CoreDatabaseTable, }; - constructor( - config: Partial, - database: SQLiteDB, - tableName: string, - primaryKeyColumns?: PrimaryKeyColumn[], - ) { - super(database, tableName, primaryKeyColumns); - - this.config = { ...this.getConfigDefaults(), ...config }; - } - /** * @inheritdoc */ async initialize(): Promise { + await super.initialize(); + this.environmentObserver = CoreEvents.on(CoreConfigProvider.ENVIRONMENT_UPDATED, async () => { if (!(await this.shouldUpdateTarget())) { return; @@ -82,9 +75,23 @@ export class CoreDatabaseTableProxy< * @inheritdoc */ async destroy(): Promise { + await super.destroy(); + this.environmentObserver?.off(); } + /** + * @inheritdoc + */ + matchesConfig(config: Partial): boolean { + const thisDebug = this.config.debug ?? false; + const thisCachingStrategy = this.config.cachingStrategy ?? this.DEFAULT_CACHING_STRATEGY; + const otherDebug = config.debug ?? false; + const otherCachingStrategy = config.cachingStrategy ?? this.DEFAULT_CACHING_STRATEGY; + + return super.matchesConfig(config) && thisDebug === otherDebug && thisCachingStrategy === otherCachingStrategy; + } + /** * @inheritdoc */ @@ -172,24 +179,12 @@ export class CoreDatabaseTableProxy< return this.target.deleteByPrimaryKey(primaryKey); } - /** - * Get default configuration values. - * - * @returns Config defaults. - */ - protected getConfigDefaults(): CoreDatabaseConfiguration { - return { - cachingStrategy: CoreDatabaseCachingStrategy.None, - debug: false, - }; - } - /** * Get database configuration to use at runtime. * * @returns Database configuration. */ - protected async getRuntimeConfig(): Promise { + protected async getRuntimeConfig(): Promise> { await CoreConfig.ready(); return { @@ -228,7 +223,8 @@ export class CoreDatabaseTableProxy< const originalTarget = target instanceof CoreDebugDatabaseTable ? target.getTarget() : target; return (config.debug && target === originalTarget) - || originalTarget?.constructor !== this.targetConstructors[config.cachingStrategy]; + || originalTarget?.constructor !== this.targetConstructors[config.cachingStrategy ?? this.DEFAULT_CACHING_STRATEGY] + || !originalTarget.matchesConfig(config); } /** @@ -238,7 +234,7 @@ export class CoreDatabaseTableProxy< */ protected async createTarget(): Promise> { const config = await this.getRuntimeConfig(); - const table = this.createTable(config.cachingStrategy); + const table = this.createTable(config); return config.debug ? new CoreDebugDatabaseTable(table) : table; } @@ -246,23 +242,29 @@ export class CoreDatabaseTableProxy< /** * Create a database table using the given caching strategy. * - * @param cachingStrategy Caching strategy. + * @param config Database configuration. * @returns Database table. */ - protected createTable(cachingStrategy: CoreDatabaseCachingStrategy): CoreDatabaseTable { - const DatabaseTable = this.targetConstructors[cachingStrategy]; + protected createTable(config: Partial): CoreDatabaseTable { + const DatabaseTable = this.targetConstructors[config.cachingStrategy ?? this.DEFAULT_CACHING_STRATEGY]; - return new DatabaseTable(this.database, this.tableName, this.primaryKeyColumns); + return new DatabaseTable(config, this.database, this.tableName, this.primaryKeyColumns); } } -/** - * Database proxy configuration. - */ -export interface CoreDatabaseConfiguration { - cachingStrategy: CoreDatabaseCachingStrategy; - debug: boolean; +declare module '@classes/database/database-table' { + + /** + * Augment CoreDatabaseConfiguration interface with data specific to this class. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreDatabaseConfiguration { + cachingStrategy: CoreDatabaseCachingStrategy; + debug: boolean; + } + } /** diff --git a/src/core/classes/database/database-table.ts b/src/core/classes/database/database-table.ts index f198b333e..5aae6cac5 100644 --- a/src/core/classes/database/database-table.ts +++ b/src/core/classes/database/database-table.ts @@ -24,16 +24,30 @@ export class CoreDatabaseTable< PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey > { + protected config: Partial; protected database: SQLiteDB; protected tableName: string; protected primaryKeyColumns: PrimaryKeyColumn[]; - constructor(database: SQLiteDB, tableName: string, primaryKeyColumns?: PrimaryKeyColumn[]) { + constructor( + config: Partial, + database: SQLiteDB, + tableName: string, + primaryKeyColumns?: PrimaryKeyColumn[], + ) { + this.config = config; this.database = database; this.tableName = tableName; this.primaryKeyColumns = primaryKeyColumns ?? ['id'] as PrimaryKeyColumn[]; } + /** + * Get database configuration. + */ + getConfig(): Partial { + return this.config; + } + /** * Get database connection. * @@ -75,6 +89,17 @@ export class CoreDatabaseTable< // Nothing to destroy by default, override this method if necessary. } + /** + * Check whether the table matches the given configuration for the values that concern it. + * + * @param config Database config. + * @returns Whether the table matches the given configuration. + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + matchesConfig(config: Partial): boolean { + return true; + } + /** * Get records matching the given conditions. * @@ -336,6 +361,13 @@ export class CoreDatabaseTable< } +/** + * Database configuration. + */ +export interface CoreDatabaseConfiguration { + // This definition is augmented in subclasses. +} + /** * CoreDatabaseTable constructor. */ @@ -346,6 +378,7 @@ export type CoreDatabaseTableConstructor< > = { new ( + config: Partial, database: SQLiteDB, tableName: string, primaryKeyColumns?: PrimaryKeyColumn[] diff --git a/src/core/classes/database/debug-database-table.ts b/src/core/classes/database/debug-database-table.ts index d7e58b56b..5ab22bb8e 100644 --- a/src/core/classes/database/debug-database-table.ts +++ b/src/core/classes/database/debug-database-table.ts @@ -37,7 +37,7 @@ export class CoreDebugDatabaseTable< protected logger: CoreLogger; constructor(target: CoreDatabaseTable) { - super(target.getDatabase(), target.getTableName(), target.getPrimaryKeyColumns()); + super(target.getConfig(), target.getDatabase(), target.getTableName(), target.getPrimaryKeyColumns()); this.target = target; this.logger = CoreLogger.getInstance(`CoreDatabase[${this.tableName}]`); @@ -53,7 +53,9 @@ export class CoreDebugDatabaseTable< /** * @inheritdoc */ - initialize(): Promise { + async initialize(): Promise { + await super.initialize(); + this.logger.log('initialize', this.target); return this.target.initialize(); @@ -62,7 +64,9 @@ export class CoreDebugDatabaseTable< /** * @inheritdoc */ - destroy(): Promise { + async destroy(): Promise { + await super.destroy(); + this.logger.log('destroy'); return this.target.destroy(); diff --git a/src/core/classes/database/eager-database-table.ts b/src/core/classes/database/eager-database-table.ts index 461485df5..97aac2f19 100644 --- a/src/core/classes/database/eager-database-table.ts +++ b/src/core/classes/database/eager-database-table.ts @@ -40,6 +40,8 @@ export class CoreEagerDatabaseTable< * @inheritdoc */ async initialize(): Promise { + await super.initialize(); + const records = await super.getMany(); this.records = records.reduce((data, record) => { diff --git a/src/core/classes/database/lazy-database-table.ts b/src/core/classes/database/lazy-database-table.ts index 489b21a54..f29bbddb7 100644 --- a/src/core/classes/database/lazy-database-table.ts +++ b/src/core/classes/database/lazy-database-table.ts @@ -14,7 +14,13 @@ import { CoreError } from '@classes/errors/error'; import { SQLiteDBRecordValues } from '@classes/sqlitedb'; -import { CoreDatabaseTable, CoreDatabaseConditions, GetDBRecordPrimaryKey, CoreDatabaseQueryOptions } from './database-table'; +import { + CoreDatabaseConfiguration, + CoreDatabaseTable, + CoreDatabaseConditions, + GetDBRecordPrimaryKey, + CoreDatabaseQueryOptions, +} from './database-table'; /** * Wrapper used to improve performance by caching records that are used often for faster read operations. @@ -28,7 +34,38 @@ export class CoreLazyDatabaseTable< PrimaryKey extends GetDBRecordPrimaryKey = GetDBRecordPrimaryKey > extends CoreDatabaseTable { + protected readonly DEFAULT_CACHE_LIFETIME = 60000; + protected records: Record = {}; + protected interval?: number; + + /** + * @inheritdoc + */ + async initialize(): Promise { + await super.initialize(); + + this.interval = window.setInterval(() => (this.records = {}), this.config.lazyCacheLifetime ?? this.DEFAULT_CACHE_LIFETIME); + } + + /** + * @inheritdoc + */ + async destroy(): Promise { + await super.destroy(); + + this.interval && window.clearInterval(this.interval); + } + + /** + * @inheritdoc + */ + matchesConfig(config: Partial): boolean { + const thisCacheLifetime = this.config.lazyCacheLifetime ?? this.DEFAULT_CACHE_LIFETIME; + const otherCacheLifetime = config.lazyCacheLifetime ?? this.DEFAULT_CACHE_LIFETIME; + + return super.matchesConfig(config) && thisCacheLifetime === otherCacheLifetime; + } /** * @inheritdoc @@ -152,3 +189,16 @@ export class CoreLazyDatabaseTable< } } + +declare module '@classes/database/database-table' { + + /** + * Augment CoreDatabaseConfiguration interface with data specific to this table. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreDatabaseConfiguration { + lazyCacheLifetime: number; + } + +} diff --git a/src/core/classes/tests/database-table.test.ts b/src/core/classes/tests/database-table.test.ts index 2cf4840b0..8e1e0f1e9 100644 --- a/src/core/classes/tests/database-table.test.ts +++ b/src/core/classes/tests/database-table.test.ts @@ -13,12 +13,8 @@ // limitations under the License. import { mock, mockSingleton } from '@/testing/utils'; -import { CoreDatabaseSorting, CoreDatabaseTable } from '@classes/database/database-table'; -import { - CoreDatabaseCachingStrategy, - CoreDatabaseConfiguration, - CoreDatabaseTableProxy, -} from '@classes/database/database-table-proxy'; +import { CoreDatabaseConfiguration, CoreDatabaseSorting, CoreDatabaseTable } from '@classes/database/database-table'; +import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; import { SQLiteDB } from '@classes/sqlitedb'; import { CoreConfig } from '@services/config'; diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index 9388baaa5..d18284bd7 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -56,12 +56,8 @@ import { CoreAjaxError } from '@classes/errors/ajaxerror'; import { CoreAjaxWSError } from '@classes/errors/ajaxwserror'; import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CorePromisedValue } from '@classes/promised-value'; -import { CoreDatabaseTable } from '@classes/database/database-table'; -import { - CoreDatabaseCachingStrategy, - CoreDatabaseConfiguration, - CoreDatabaseTableProxy, -} from '@classes/database/database-table-proxy'; +import { CoreDatabaseConfiguration, CoreDatabaseTable } from '@classes/database/database-table'; +import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; import { asyncInstance, AsyncInstance } from '../utils/async-instance'; import { CoreConfig } from './config'; diff --git a/src/types/config.d.ts b/src/types/config.d.ts index c8fa468bc..ef8c23349 100644 --- a/src/types/config.d.ts +++ b/src/types/config.d.ts @@ -17,7 +17,7 @@ import { CoreMainMenuLocalizedCustomItem } from '@features/mainmenu/services/mai import { CoreSitesDemoSiteData } from '@services/sites'; import { OpenFileAction } from '@services/utils/utils'; import { CoreLoginSiteSelectorListMethod } from '@features/login/services/login-helper'; -import { CoreDatabaseConfiguration } from '@classes/database/database-table-proxy'; +import { CoreDatabaseConfiguration } from '@classes/database/database-table'; /* eslint-disable @typescript-eslint/naming-convention */