MOBILE-3977 core: Optimize wscache table

main
Noel De Martin 2022-02-03 13:43:10 +01:00
parent a65919debc
commit 7a1dfa38bd
3 changed files with 95 additions and 44 deletions

View File

@ -41,6 +41,7 @@ import { CoreLogger } from '@singletons/logger';
import { Translate } from '@singletons';
import { CoreIonLoadingElement } from './ion-loading';
import { CoreLang } from '@services/lang';
import { CoreSites } from '@services/sites';
/**
* QR Code type enumeration.
@ -920,20 +921,20 @@ export class CoreSite {
preSets: CoreSiteWSPreSets,
emergency?: boolean,
): Promise<T> {
const db = this.db;
if (!db || !preSets.getFromCache) {
if (!this.db || !preSets.getFromCache) {
throw new CoreError('Get from cache is disabled.');
}
const id = this.getCacheId(method, data);
const cacheTable = await CoreSites.getCacheTable(this);
let entry: CoreSiteWSCacheRecord | undefined;
if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) {
const entries = await db.getRecords<CoreSiteWSCacheRecord>(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey });
const entries = await cacheTable.all({ key: preSets.cacheKey });
if (!entries.length) {
// Cache key not found, get by params sent.
entry = await db.getRecord(CoreSite.WS_CACHE_TABLE, { id });
entry = await cacheTable.findByPrimaryKey({ id });
} else {
if (entries.length > 1) {
// More than one entry found. Search the one with same ID as this call.
@ -945,7 +946,7 @@ export class CoreSite {
}
}
} else {
entry = await db.getRecord(CoreSite.WS_CACHE_TABLE, { id });
entry = await cacheTable.findByPrimaryKey({ id });
}
if (entry === undefined) {
@ -990,18 +991,25 @@ export class CoreSite {
*/
async getComponentCacheSize(component: string, componentId?: number): Promise<number> {
const params: Array<string | number> = [component];
const cacheTable = await CoreSites.getCacheTable(this);
let extraClause = '';
if (componentId !== undefined && componentId !== null) {
params.push(componentId);
extraClause = ' AND componentId = ?';
}
const size = <number> await this.getDb().getFieldSql(
'SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE + ' WHERE component = ?' + extraClause,
params,
return cacheTable.reduce(
{
sql: 'SUM(length(data))',
js: (size, record) => size + record.data.length,
jsInitialValue: 0,
},
{
sql: 'WHERE component = ?' + extraClause,
sqlParams: params,
js: record => record.component === component && (params.length === 1 || record.componentId === componentId),
},
);
return size;
}
/**
@ -1015,10 +1023,6 @@ export class CoreSite {
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected async saveToCache(method: string, data: any, response: any, preSets: CoreSiteWSPreSets): Promise<void> {
if (!this.db) {
throw new CoreError('Site DB not initialized.');
}
if (preSets.uniqueCacheKey) {
// Cache key must be unique, delete all entries with same cache key.
await CoreUtils.ignoreErrors(this.deleteFromCache(method, data, preSets, true));
@ -1027,6 +1031,7 @@ export class CoreSite {
// Since 3.7, the expiration time contains the time the entry is modified instead of the expiration time.
// We decided to reuse this field to prevent modifying the database table.
const id = this.getCacheId(method, data);
const cacheTable = await CoreSites.getCacheTable(this);
const entry = {
id,
data: JSON.stringify(response),
@ -1044,7 +1049,7 @@ export class CoreSite {
}
}
await this.db.insertRecord(CoreSite.WS_CACHE_TABLE, entry);
await cacheTable.insert(entry);
}
/**
@ -1058,16 +1063,13 @@ export class CoreSite {
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected async deleteFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, allCacheKey?: boolean): Promise<void> {
if (!this.db) {
throw new CoreError('Site DB not initialized.');
}
const id = this.getCacheId(method, data);
const cacheTable = await CoreSites.getCacheTable(this);
if (allCacheKey) {
await this.db.deleteRecords(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey });
await cacheTable.delete({ key: preSets.cacheKey });
} else {
await this.db.deleteRecords(CoreSite.WS_CACHE_TABLE, { id });
await cacheTable.deleteByPrimaryKey({ id });
}
}
@ -1084,18 +1086,14 @@ export class CoreSite {
return;
}
if (!this.db) {
throw new CoreError('Site DB not initialized');
}
const params = { component };
const cacheTable = await CoreSites.getCacheTable(this);
const params = {
component,
};
if (componentId) {
params['componentId'] = componentId;
}
await this.db.deleteRecords(CoreSite.WS_CACHE_TABLE, params);
await cacheTable.delete(params);
}
/*
@ -1127,14 +1125,12 @@ export class CoreSite {
* @return Promise resolved when the cache entries are invalidated.
*/
async invalidateWsCache(): Promise<void> {
if (!this.db) {
throw new CoreError('Site DB not initialized');
}
this.logger.debug('Invalidate all the cache for site: ' + this.id);
try {
await this.db.updateRecords(CoreSite.WS_CACHE_TABLE, { expirationTime: 0 });
const cacheTable = await CoreSites.getCacheTable(this);
await cacheTable.update({ expirationTime: 0 });
} finally {
CoreEvents.trigger(CoreEvents.WS_CACHE_INVALIDATED, {}, this.getId());
}
@ -1147,16 +1143,15 @@ export class CoreSite {
* @return Promise resolved when the cache entries are invalidated.
*/
async invalidateWsCacheForKey(key: string): Promise<void> {
if (!this.db) {
throw new CoreError('Site DB not initialized');
}
if (!key) {
return;
}
this.logger.debug('Invalidate cache for key: ' + key);
await this.db.updateRecords(CoreSite.WS_CACHE_TABLE, { expirationTime: 0 }, { key });
const cacheTable = await CoreSites.getCacheTable(this);
await cacheTable.update({ expirationTime: 0 }, { key });
}
/**
@ -1184,18 +1179,19 @@ export class CoreSite {
* @return Promise resolved when the cache entries are invalidated.
*/
async invalidateWsCacheForKeyStartingWith(key: string): Promise<void> {
if (!this.db) {
throw new CoreError('Site DB not initialized');
}
if (!key) {
return;
}
this.logger.debug('Invalidate cache for key starting with: ' + key);
const sql = 'UPDATE ' + CoreSite.WS_CACHE_TABLE + ' SET expirationTime=0 WHERE key LIKE ?';
const cacheTable = await CoreSites.getCacheTable(this);
await this.db.execute(sql, [key + '%']);
await cacheTable.updateWhere({ expirationTime: 0 }, {
sql: 'key LIKE ?',
sqlParams: [key],
js: record => !!record.key?.startsWith(key),
});
}
/**
@ -1270,9 +1266,13 @@ export class CoreSite {
* @return Promise resolved with the total size of all data in the cache table (bytes)
*/
async getCacheUsage(): Promise<number> {
const size = <number> await this.getDb().getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE);
const cacheTable = await CoreSites.getCacheTable(this);
return size;
return cacheTable.reduce({
sql: 'SUM(length(data))',
js: (size, record) => size + record.data.length,
jsInitialValue: 0,
});
}
/**

View File

@ -15,11 +15,13 @@
import { CoreFilepool } from '@services/filepool';
import { CoreLang } from '@services/lang';
import { CoreLocalNotifications } from '@services/local-notifications';
import { CoreSites } from '@services/sites';
import { CoreUpdateManager } from '@services/update-manager';
export default async function(): Promise<void> {
await Promise.all([
CoreFilepool.initialize(),
CoreSites.initialize(),
CoreLang.initialize(),
CoreLocalNotifications.initialize(),
CoreUpdateManager.initialize(),

View File

@ -31,6 +31,7 @@ import {
CoreSiteConfig,
CoreSitePublicConfigResponse,
CoreSiteInfoResponse,
CoreSiteWSCacheRecord,
} from '@classes/site';
import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb';
import { CoreError } from '@classes/errors/error';
@ -57,6 +58,9 @@ import { CoreErrorWithTitle } from '@classes/errors/errorwithtitle';
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, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy';
export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS');
@ -85,6 +89,7 @@ export class CoreSitesProvider {
// Variables for DB.
protected appDB: Promise<SQLiteDB>;
protected resolveAppDB!: (appDB: SQLiteDB) => void;
protected cacheTables: Record<string, CorePromisedValue<CoreDatabaseTable<CoreSiteWSCacheRecord>>> = {};
constructor(@Optional() @Inject(CORE_SITE_SCHEMAS) siteSchemas: CoreSiteSchema[][] = []) {
this.appDB = new Promise(resolve => this.resolveAppDB = resolve);
@ -99,6 +104,23 @@ export class CoreSitesProvider {
);
}
/**
* Initialize.
*/
initialize(): void {
CoreEvents.on(CoreEvents.SITE_DELETED, async ({ siteId }) => {
if (!siteId || !(siteId in this.cacheTables)) {
return;
}
const cacheTable = await this.cacheTables[siteId];
delete this.cacheTables[siteId];
await cacheTable.destroy();
});
}
/**
* Initialize database.
*/
@ -112,6 +134,33 @@ export class CoreSitesProvider {
this.resolveAppDB(CoreApp.getDB());
}
/**
* Get cache table.
*
* @param siteId Site id.
* @returns cache table.
*/
async getCacheTable(site: CoreSite): Promise<CoreDatabaseTable<CoreSiteWSCacheRecord>> {
if (!site.id) {
throw new CoreError('Can\'t get cache table for site without id');
}
if (!(site.id in this.cacheTables)) {
const promisedTable = this.cacheTables[site.id] = new CorePromisedValue();
const table = new CoreDatabaseTableProxy<CoreSiteWSCacheRecord>(
{ cachingStrategy: CoreDatabaseCachingStrategy.None },
site.getDb(),
CoreSite.WS_CACHE_TABLE,
);
await table.initialize();
promisedTable.resolve(table);
}
return this.cacheTables[site.id];
}
/**
* Get the demo data for a certain "name" if it is a demo site.
*