Merge pull request #3127 from NoelDeMartin/MOBILE-3821

MOBILE-3821: Database optimization final tweaks (before 4.0)
main
Dani Palou 2022-02-21 12:51:12 +01:00 committed by GitHub
commit 5b0abf40f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 282 additions and 123 deletions

View File

@ -54,18 +54,21 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler,
protected unreadCount = 0;
protected contactRequestsCount = 0;
protected orMore = false;
protected badgeCount?: number;
constructor() {
CoreEvents.on(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, (data) => {
this.unreadCount = data.favourites + data.individual + data.group + data.self;
this.orMore = !!data.orMore;
this.updateBadge(data.siteId!);
data.siteId && this.updateBadge(data.siteId);
});
CoreEvents.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => {
this.contactRequestsCount = data.count;
this.updateBadge(data.siteId!);
data.siteId && this.updateBadge(data.siteId);
});
// Reset info on logout.
@ -123,27 +126,28 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler,
* @return Resolve when done.
*/
async refreshBadge(siteId?: string, unreadOnly?: boolean): Promise<void> {
siteId = siteId || CoreSites.getCurrentSiteId();
if (!siteId) {
const badgeSiteId = siteId || CoreSites.getCurrentSiteId();
if (!badgeSiteId) {
return;
}
const promises: Promise<unknown>[] = [];
promises.push(AddonMessages.refreshUnreadConversationCounts(siteId).catch(() => {
promises.push(AddonMessages.refreshUnreadConversationCounts(badgeSiteId).catch(() => {
this.unreadCount = 0;
this.orMore = false;
}));
// Refresh the number of contact requests in 3.6+ sites.
if (!unreadOnly && AddonMessages.isGroupMessagingEnabled()) {
promises.push(AddonMessages.refreshContactRequestsCount(siteId).catch(() => {
promises.push(AddonMessages.refreshContactRequestsCount(badgeSiteId).catch(() => {
this.contactRequestsCount = 0;
}));
}
await Promise.all(promises).finally(() => {
this.updateBadge(siteId!);
this.updateBadge(badgeSiteId);
this.handler.loading = false;
});
}
@ -155,6 +159,13 @@ export class AddonMessagesMainMenuHandlerService implements CoreMainMenuHandler,
*/
updateBadge(siteId: string): void {
const totalCount = this.unreadCount + (this.contactRequestsCount || 0);
if (this.badgeCount === totalCount) {
return;
}
this.badgeCount = totalCount;
if (totalCount > 0) {
this.handler.badge = totalCount + (this.orMore ? '+' : '');
} else {

View File

@ -69,6 +69,7 @@ export class AddonModBookProvider {
() => CoreSites.getSiteTable(LAST_CHAPTER_VIEWED_TABLE, {
siteId,
config: { cachingStrategy: CoreDatabaseCachingStrategy.None },
onDestroy: () => delete this.lastChapterViewedTables[siteId],
}),
),
);

View File

@ -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<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
> extends CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
protected config: CoreDatabaseConfiguration;
protected readonly DEFAULT_CACHING_STRATEGY = CoreDatabaseCachingStrategy.None;
protected target = asyncInstance<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>>();
protected environmentObserver?: CoreEventObserver;
protected targetConstructors: Record<
@ -52,21 +54,12 @@ export class CoreDatabaseTableProxy<
[CoreDatabaseCachingStrategy.None]: CoreDatabaseTable,
};
constructor(
config: Partial<CoreDatabaseConfiguration>,
database: SQLiteDB,
tableName: string,
primaryKeyColumns?: PrimaryKeyColumn[],
) {
super(database, tableName, primaryKeyColumns);
this.config = { ...this.getConfigDefaults(), ...config };
}
/**
* @inheritdoc
*/
async initialize(): Promise<void> {
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<void> {
await super.destroy();
this.environmentObserver?.off();
}
/**
* @inheritdoc
*/
matchesConfig(config: Partial<CoreDatabaseConfiguration>): 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<CoreDatabaseConfiguration> {
protected async getRuntimeConfig(): Promise<Partial<CoreDatabaseConfiguration>> {
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<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>> {
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<DBRecord, PrimaryKeyColumn> {
const DatabaseTable = this.targetConstructors[cachingStrategy];
protected createTable(config: Partial<CoreDatabaseConfiguration>): CoreDatabaseTable<DBRecord, PrimaryKeyColumn> {
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;
}
}
/**

View File

@ -24,16 +24,31 @@ export class CoreDatabaseTable<
PrimaryKey extends GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
> {
protected config: Partial<CoreDatabaseConfiguration>;
protected database: SQLiteDB;
protected tableName: string;
protected primaryKeyColumns: PrimaryKeyColumn[];
protected listeners: CoreDatabaseTableListener[] = [];
constructor(database: SQLiteDB, tableName: string, primaryKeyColumns?: PrimaryKeyColumn[]) {
constructor(
config: Partial<CoreDatabaseConfiguration>,
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<CoreDatabaseConfiguration> {
return this.config;
}
/**
* Get database connection.
*
@ -72,7 +87,27 @@ export class CoreDatabaseTable<
* Destroy.
*/
async destroy(): Promise<void> {
// Nothing to destroy by default, override this method if necessary.
this.listeners.forEach(listener => listener.onDestroy?.());
}
/**
* Add listener.
*
* @param listener Listener.
*/
addListener(listener: CoreDatabaseTableListener): void {
this.listeners.push(listener);
}
/**
* 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<CoreDatabaseConfiguration>): boolean {
return true;
}
/**
@ -336,6 +371,20 @@ export class CoreDatabaseTable<
}
/**
* Database configuration.
*/
export interface CoreDatabaseConfiguration {
// This definition is augmented in subclasses.
}
/**
* Database table listener.
*/
export interface CoreDatabaseTableListener {
onDestroy?(): void;
}
/**
* CoreDatabaseTable constructor.
*/
@ -346,6 +395,7 @@ export type CoreDatabaseTableConstructor<
> = {
new (
config: Partial<CoreDatabaseConfiguration>,
database: SQLiteDB,
tableName: string,
primaryKeyColumns?: PrimaryKeyColumn[]

View File

@ -37,7 +37,7 @@ export class CoreDebugDatabaseTable<
protected logger: CoreLogger;
constructor(target: CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey>) {
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<void> {
async initialize(): Promise<void> {
await super.initialize();
this.logger.log('initialize', this.target);
return this.target.initialize();
@ -62,7 +64,9 @@ export class CoreDebugDatabaseTable<
/**
* @inheritdoc
*/
destroy(): Promise<void> {
async destroy(): Promise<void> {
await super.destroy();
this.logger.log('destroy');
return this.target.destroy();

View File

@ -40,6 +40,8 @@ export class CoreEagerDatabaseTable<
* @inheritdoc
*/
async initialize(): Promise<void> {
await super.initialize();
const records = await super.getMany();
this.records = records.reduce((data, record) => {

View File

@ -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<DBRecord, PrimaryKeyColumn> = GetDBRecordPrimaryKey<DBRecord, PrimaryKeyColumn>
> extends CoreDatabaseTable<DBRecord, PrimaryKeyColumn, PrimaryKey> {
protected readonly DEFAULT_CACHE_LIFETIME = 60000;
protected records: Record<string, DBRecord | null> = {};
protected interval?: number;
/**
* @inheritdoc
*/
async initialize(): Promise<void> {
await super.initialize();
this.interval = window.setInterval(() => (this.records = {}), this.config.lazyCacheLifetime ?? this.DEFAULT_CACHE_LIFETIME);
}
/**
* @inheritdoc
*/
async destroy(): Promise<void> {
await super.destroy();
this.interval && window.clearInterval(this.interval);
}
/**
* @inheritdoc
*/
matchesConfig(config: Partial<CoreDatabaseConfiguration>): 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;
}
}

View File

@ -1170,27 +1170,54 @@ export class SQLiteDB {
*/
protected getDatabaseSpies(db: SQLiteObject): Partial<SQLiteObject> {
return {
executeSql(statement, params) {
async executeSql(statement, params) {
const start = performance.now();
return db.executeSql(statement, params).then(result => {
CoreDB.logQuery(statement, performance.now() - start, params);
try {
const result = await db.executeSql(statement, params);
CoreDB.logQuery({
params,
sql: statement,
duration: performance.now() - start,
});
return result;
});
} catch (error) {
CoreDB.logQuery({
params,
error,
sql: statement,
duration: performance.now() - start,
});
throw error;
}
},
sqlBatch(statements) {
async sqlBatch(statements) {
const start = performance.now();
const sql = Array.isArray(statements)
? statements.join(' | ')
: String(statements);
return db.sqlBatch(statements).then(result => {
const sql = Array.isArray(statements)
? statements.join(' | ')
: String(statements);
try {
const result = await db.sqlBatch(statements);
CoreDB.logQuery(sql, performance.now() - start);
CoreDB.logQuery({
sql,
duration: performance.now() - start,
});
return result;
});
} catch (error) {
CoreDB.logQuery({
sql,
error,
duration: performance.now() - start,
});
throw error;
}
},
};
}

View File

@ -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';

View File

@ -148,6 +148,7 @@ export class CoreCourseProvider {
() => CoreSites.getSiteTable(COURSE_STATUS_TABLE, {
siteId,
config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager },
onDestroy: () => delete this.statusTables[siteId],
}),
),
);

View File

@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/* tslint:disable:no-console */
import { SQLiteDB } from '@classes/sqlitedb';
import { DbTransaction, SQLiteObject } from '@ionic-native/sqlite/ngx';
import { CoreDB } from '@services/db';
@ -53,7 +51,7 @@ export class SQLiteDBMock extends SQLiteDB {
await this.ready();
return new Promise((resolve, reject): void => {
this.db!.transaction((tx) => {
this.db?.transaction((tx) => {
// Query all tables from sqlite_master that we have created and can modify.
const args = [];
const query = `SELECT * FROM sqlite_master
@ -99,15 +97,13 @@ export class SQLiteDBMock extends SQLiteDB {
return new Promise((resolve, reject): void => {
// With WebSQL, all queries must be run in a transaction.
this.db!.transaction((tx) => {
tx.executeSql(sql, params, (tx, results) => {
resolve(results);
}, (tx, error) => {
// eslint-disable-next-line no-console
console.error(sql, params, error);
reject(error);
});
this.db?.transaction((tx) => {
tx.executeSql(
sql,
params,
(_, results) => resolve(results),
(_, error) => reject(new Error(`SQL failed: ${sql}, reason: ${error?.message}`)),
);
});
});
}
@ -126,7 +122,7 @@ export class SQLiteDBMock extends SQLiteDB {
return new Promise((resolve, reject): void => {
// Create a transaction to execute the queries.
this.db!.transaction((tx) => {
this.db?.transaction((tx) => {
const promises: Promise<void>[] = [];
// Execute all the queries. Each statement can be a string or an array.
@ -143,14 +139,7 @@ export class SQLiteDBMock extends SQLiteDB {
params = null;
}
tx.executeSql(query, params, (tx, results) => {
resolve(results);
}, (tx, error) => {
// eslint-disable-next-line no-console
console.error(query, params, error);
reject(error);
});
tx.executeSql(query, params, (_, results) => resolve(results), (_, error) => reject(error));
}));
});
@ -187,13 +176,30 @@ export class SQLiteDBMock extends SQLiteDB {
const transactionSpy: DbTransaction = {
executeSql(sql, params, success, error) {
const start = performance.now();
const resolve = callback => (...args) => {
CoreDB.logQuery(sql, performance.now() - start, params);
return callback(...args);
};
return transaction.executeSql(
sql,
params,
(...args) => {
CoreDB.logQuery({
sql,
params,
duration: performance.now() - start,
});
return transaction.executeSql(sql, params, resolve(success), resolve(error));
return success?.(...args);
},
(...args) => {
CoreDB.logQuery({
sql,
params,
error: args[0],
duration: performance.now() - start,
});
return error?.(...args);
},
);
},
};

View File

@ -74,6 +74,7 @@ export class CorePushNotificationsProvider {
siteId,
config: { cachingStrategy: CoreDatabaseCachingStrategy.None },
primaryKeyColumns: ['appid', 'uuid'],
onDestroy: () => delete this.registeredDevicesTables[siteId],
},
),
),

View File

@ -13,14 +13,20 @@
// limitations under the License.
import { CoreConfig, CoreConfigProvider } from '@services/config';
import { CoreDB, CoreDbProvider } from '@services/db';
import { CoreCustomURLSchemes, CoreCustomURLSchemesProvider } from '@services/urlschemes';
import { CoreConstants } from '../constants';
type DevelopmentWindow = Window & {
configProvider?: CoreConfigProvider;
dbProvider?: CoreDbProvider;
urlSchemes?: CoreCustomURLSchemesProvider;
};
function initializeDevelopmentWindow(window: DevelopmentWindow) {
window.configProvider = CoreConfig.instance;
window.dbProvider = CoreDB.instance;
window.urlSchemes = CoreCustomURLSchemes.instance;
}
export default function(): void {

View File

@ -18,6 +18,7 @@ import { SQLiteDB } from '@classes/sqlitedb';
import { SQLiteDBMock } from '@features/emulator/classes/sqlitedb';
import { makeSingleton, SQLite, Platform } from '@singletons';
import { CoreAppProvider } from './app';
import { CoreUtils } from './utils/utils';
/**
* This service allows interacting with the local database to store and retrieve data.
@ -35,15 +36,21 @@ export class CoreDbProvider {
* @returns Whether queries should be logged.
*/
loggingEnabled(): boolean {
return CoreAppProvider.isAutomated();
return CoreUtils.hasCookie('MoodleAppDBLoggingEnabled') || CoreAppProvider.isAutomated();
}
/**
* Print query history in console.
*
* @param format Log format, with the following substitutions: :sql, :duration, and :result.
*/
printHistory(): void {
const substituteParams = ({ sql, params }: CoreDbQueryLog) =>
Object.values(params ?? []).reduce((sql: string, param: string) => sql.replace('?', param), sql);
printHistory(format: string = ':sql | Duration: :duration | Result: :result'): void {
const substituteParams = ({ sql, params, duration, error }: CoreDbQueryLog) => format
.replace(':sql', Object
.values(params ?? [])
.reduce((sql: string, param: string) => sql.replace('?', param) as string, sql) as string)
.replace(':duration', `${Math.round(duration).toString().padStart(4, '0')}ms`)
.replace(':result', error?.message ?? 'Success');
// eslint-disable-next-line no-console
console.log(this.queryLogs.map(substituteParams).join('\n'));
@ -52,11 +59,10 @@ export class CoreDbProvider {
/**
* Log a query.
*
* @param sql Query SQL.
* @param params Query parameters.
* @param log Query log.
*/
logQuery(sql: string, duration: number, params?: unknown[]): void {
this.queryLogs.push({ sql, duration, params });
logQuery(log: CoreDbQueryLog): void {
this.queryLogs.push(log);
}
/**
@ -121,5 +127,6 @@ export const CoreDB = makeSingleton(CoreDbProvider);
export interface CoreDbQueryLog {
sql: string;
duration: number;
error?: Error;
params?: unknown[];
}

View File

@ -113,6 +113,7 @@ export class CoreFilepoolProvider {
siteId,
config: { cachingStrategy: CoreDatabaseCachingStrategy.Lazy },
primaryKeyColumns: ['fileId'],
onDestroy: () => delete this.filesTables[siteId],
}),
),
);
@ -122,6 +123,7 @@ export class CoreFilepoolProvider {
siteId,
config: { cachingStrategy: CoreDatabaseCachingStrategy.Lazy },
primaryKeyColumns: ['fileId', 'component', 'componentId'],
onDestroy: () => delete this.linksTables[siteId],
}),
),
);
@ -130,6 +132,7 @@ export class CoreFilepoolProvider {
() => CoreSites.getSiteTable<CoreFilepoolPackageEntry, 'id'>(PACKAGES_TABLE_NAME, {
siteId,
config: { cachingStrategy: CoreDatabaseCachingStrategy.Lazy },
onDestroy: () => delete this.packagesTables[siteId],
}),
),
);
@ -149,16 +152,6 @@ export class CoreFilepoolProvider {
NgZone.run(() => this.checkQueueProcessing());
});
});
CoreEvents.on(CoreEvents.SITE_DELETED, async ({ siteId }) => {
if (!siteId || !(siteId in this.filesTables)) {
return;
}
await this.filesTables[siteId].destroy();
delete this.filesTables[siteId];
});
}
/**

View File

@ -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';
@ -162,6 +158,7 @@ export class CoreSitesProvider {
config: Partial<CoreDatabaseConfiguration>;
database: SQLiteDB;
primaryKeyColumns: PrimaryKeyColumn[];
onDestroy(): void;
}> = {},
): Promise<CoreDatabaseTable<DBRecord, PrimaryKeyColumn>> {
const siteId = options.siteId ?? this.getCurrentSiteId();
@ -180,6 +177,8 @@ export class CoreSitesProvider {
options.primaryKeyColumns,
);
options.onDestroy && table.addListener({ onDestroy: options.onDestroy });
await table.initialize();
promisedTable.resolve(table as unknown as CoreDatabaseTable);
@ -1837,16 +1836,19 @@ export class CoreSitesProvider {
* @returns Scehmas Table.
*/
protected getSiteSchemasTable(site: CoreSite): AsyncInstance<CoreDatabaseTable<SchemaVersionsDBEntry, 'name'>> {
this.schemasTables[site.getId()] = this.schemasTables[site.getId()] ?? asyncInstance(
const siteId = site.getId();
this.schemasTables[siteId] = this.schemasTables[siteId] ?? asyncInstance(
() => this.getSiteTable(SCHEMA_VERSIONS_TABLE_NAME, {
siteId: site.getId(),
siteId: siteId,
database: site.getDb(),
config: { cachingStrategy: CoreDatabaseCachingStrategy.Eager },
primaryKeyColumns: ['name'],
onDestroy: () => delete this.schemasTables[siteId],
}),
);
return this.schemasTables[site.getId()];
return this.schemasTables[siteId];
}
}

View File

@ -32,7 +32,7 @@ export class CoreObject {
* @param b Second object.
* @return Whether objects are equal.
*/
static deepEquals(a: unknown, b: unknown): boolean {
static deepEquals<T=unknown>(a: T, b: T): boolean {
return JSON.stringify(a) === JSON.stringify(b);
}

View File

@ -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 */