Add fail on metadata cache miss (#7118)
- avoid failing when missing cache (used for command) - remove unused load cache function. Cache will be always re-created when trying to fetch if not existing
This commit is contained in:
@ -47,9 +47,9 @@ export class WorkspaceSchemaFactory {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (currentCacheVersion === undefined) {
|
if (currentCacheVersion === undefined) {
|
||||||
await this.workspaceMetadataCacheService.recomputeMetadataCache(
|
await this.workspaceMetadataCacheService.recomputeMetadataCache({
|
||||||
authContext.workspace.id,
|
workspaceId: authContext.workspace.id,
|
||||||
);
|
});
|
||||||
throw new GraphqlQueryRunnerException(
|
throw new GraphqlQueryRunnerException(
|
||||||
'Metadata cache version not found',
|
'Metadata cache version not found',
|
||||||
GraphqlQueryRunnerExceptionCode.METADATA_CACHE_VERSION_NOT_FOUND,
|
GraphqlQueryRunnerExceptionCode.METADATA_CACHE_VERSION_NOT_FOUND,
|
||||||
@ -63,9 +63,9 @@ export class WorkspaceSchemaFactory {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!objectMetadataMap) {
|
if (!objectMetadataMap) {
|
||||||
await this.workspaceMetadataCacheService.recomputeMetadataCache(
|
await this.workspaceMetadataCacheService.recomputeMetadataCache({
|
||||||
authContext.workspace.id,
|
workspaceId: authContext.workspace.id,
|
||||||
);
|
});
|
||||||
throw new GraphqlQueryRunnerException(
|
throw new GraphqlQueryRunnerException(
|
||||||
'Object metadata collection not found',
|
'Object metadata collection not found',
|
||||||
GraphqlQueryRunnerExceptionCode.METADATA_CACHE_VERSION_NOT_FOUND,
|
GraphqlQueryRunnerExceptionCode.METADATA_CACHE_VERSION_NOT_FOUND,
|
||||||
|
|||||||
@ -26,10 +26,13 @@ export class WorkspaceMetadataCacheService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
@LogExecutionTime()
|
@LogExecutionTime()
|
||||||
async recomputeMetadataCache(
|
async recomputeMetadataCache({
|
||||||
workspaceId: string,
|
workspaceId,
|
||||||
force = false,
|
ignoreLock = false,
|
||||||
): Promise<void> {
|
}: {
|
||||||
|
workspaceId: string;
|
||||||
|
ignoreLock?: boolean;
|
||||||
|
}): Promise<void> {
|
||||||
const currentCacheVersion =
|
const currentCacheVersion =
|
||||||
await this.getMetadataVersionFromCache(workspaceId);
|
await this.getMetadataVersionFromCache(workspaceId);
|
||||||
|
|
||||||
@ -43,17 +46,13 @@ export class WorkspaceMetadataCacheService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!force && currentCacheVersion === currentDatabaseVersion) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isAlreadyCaching =
|
const isAlreadyCaching =
|
||||||
await this.workspaceCacheStorageService.getObjectMetadataOngoingCachingLock(
|
await this.workspaceCacheStorageService.getObjectMetadataOngoingCachingLock(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
currentDatabaseVersion,
|
currentDatabaseVersion,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isAlreadyCaching) {
|
if (!ignoreLock && isAlreadyCaching) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,8 +45,8 @@ export class WorkspaceMetadataVersionService {
|
|||||||
{ metadataVersion: newMetadataVersion },
|
{ metadataVersion: newMetadataVersion },
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.workspaceMetadataCacheService.recomputeMetadataCache(
|
await this.workspaceMetadataCacheService.recomputeMetadataCache({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { EntitySchema } from 'typeorm';
|
|||||||
|
|
||||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||||
|
import { ObjectMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||||
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||||
import {
|
import {
|
||||||
@ -33,14 +34,27 @@ export class WorkspaceDatasourceFactory {
|
|||||||
public async create(
|
public async create(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
workspaceMetadataVersion: number | null,
|
workspaceMetadataVersion: number | null,
|
||||||
|
failOnMetadataCacheMiss = true,
|
||||||
): Promise<WorkspaceDataSource> {
|
): Promise<WorkspaceDataSource> {
|
||||||
const desiredWorkspaceMetadataVersion =
|
const {
|
||||||
await this.computeDesiredWorkspaceMetadataVersion(
|
metadataVersion: cachedWorkspaceMetadataVersion,
|
||||||
workspaceId,
|
objectMetadataMap: cachedObjectMetadataMap,
|
||||||
workspaceMetadataVersion,
|
} = await this.getWorkspaceMetadataFromCache(
|
||||||
);
|
workspaceId,
|
||||||
|
failOnMetadataCacheMiss,
|
||||||
|
);
|
||||||
|
|
||||||
const cacheKey = `${workspaceId}-${desiredWorkspaceMetadataVersion}`;
|
if (
|
||||||
|
workspaceMetadataVersion !== null &&
|
||||||
|
cachedWorkspaceMetadataVersion !== workspaceMetadataVersion
|
||||||
|
) {
|
||||||
|
throw new TwentyORMException(
|
||||||
|
`Workspace metadata version mismatch detected for workspace ${workspaceId}. Current version: ${cachedWorkspaceMetadataVersion}. Desired version: ${workspaceMetadataVersion}`,
|
||||||
|
TwentyORMExceptionCode.METADATA_VERSION_MISMATCH,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheKey = `${workspaceId}-${cachedWorkspaceMetadataVersion}`;
|
||||||
|
|
||||||
if (cacheKey in this.cachedDatasourcePromise) {
|
if (cacheKey in this.cachedDatasourcePromise) {
|
||||||
return this.cachedDatasourcePromise[cacheKey];
|
return this.cachedDatasourcePromise[cacheKey];
|
||||||
@ -52,25 +66,8 @@ export class WorkspaceDatasourceFactory {
|
|||||||
cacheKey as '`${string}-${string}`',
|
cacheKey as '`${string}-${string}`',
|
||||||
async () => {
|
async () => {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`Creating workspace data source for workspace ${workspaceId} and metadata version ${desiredWorkspaceMetadataVersion}`,
|
`Creating workspace data source for workspace ${workspaceId} and metadata version ${cachedWorkspaceMetadataVersion}`,
|
||||||
);
|
);
|
||||||
const cachedObjectMetadataMap =
|
|
||||||
await this.workspaceCacheStorageService.getObjectMetadataMap(
|
|
||||||
workspaceId,
|
|
||||||
desiredWorkspaceMetadataVersion,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!cachedObjectMetadataMap) {
|
|
||||||
await this.workspaceMetadataCacheService.recomputeMetadataCache(
|
|
||||||
workspaceId,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
throw new TwentyORMException(
|
|
||||||
`Object metadata map not found for workspace ${workspaceId}`,
|
|
||||||
TwentyORMExceptionCode.METADATA_COLLECTION_NOT_FOUND,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataSourceMetadata =
|
const dataSourceMetadata =
|
||||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceId(
|
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceId(
|
||||||
@ -87,7 +84,7 @@ export class WorkspaceDatasourceFactory {
|
|||||||
const cachedEntitySchemaOptions =
|
const cachedEntitySchemaOptions =
|
||||||
await this.workspaceCacheStorageService.getORMEntitySchema(
|
await this.workspaceCacheStorageService.getORMEntitySchema(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
desiredWorkspaceMetadataVersion,
|
cachedWorkspaceMetadataVersion,
|
||||||
);
|
);
|
||||||
|
|
||||||
let cachedEntitySchemas: EntitySchema[];
|
let cachedEntitySchemas: EntitySchema[];
|
||||||
@ -101,7 +98,7 @@ export class WorkspaceDatasourceFactory {
|
|||||||
Object.values(cachedObjectMetadataMap).map((objectMetadata) =>
|
Object.values(cachedObjectMetadataMap).map((objectMetadata) =>
|
||||||
this.entitySchemaFactory.create(
|
this.entitySchemaFactory.create(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
desiredWorkspaceMetadataVersion,
|
cachedWorkspaceMetadataVersion,
|
||||||
objectMetadata,
|
objectMetadata,
|
||||||
cachedObjectMetadataMap,
|
cachedObjectMetadataMap,
|
||||||
),
|
),
|
||||||
@ -110,7 +107,7 @@ export class WorkspaceDatasourceFactory {
|
|||||||
|
|
||||||
await this.workspaceCacheStorageService.setORMEntitySchema(
|
await this.workspaceCacheStorageService.setORMEntitySchema(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
desiredWorkspaceMetadataVersion,
|
cachedWorkspaceMetadataVersion,
|
||||||
entitySchemas.map((entitySchema) => entitySchema.options),
|
entitySchemas.map((entitySchema) => entitySchema.options),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -175,45 +172,67 @@ export class WorkspaceDatasourceFactory {
|
|||||||
return creationPromise;
|
return creationPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async computeDesiredWorkspaceMetadataVersion(
|
public async destroy(workspaceId: string): Promise<void> {
|
||||||
|
const cachedWorkspaceMetadataVersion =
|
||||||
|
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
|
||||||
|
|
||||||
|
await this.cacheManager.clearKey(
|
||||||
|
`${workspaceId}-${cachedWorkspaceMetadataVersion}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getWorkspaceMetadataFromCache(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
workspaceMetadataVersion: number | null,
|
failOnMetadataCacheMiss = true,
|
||||||
): Promise<number> {
|
): Promise<{
|
||||||
const latestWorkspaceMetadataVersion =
|
metadataVersion: number;
|
||||||
|
objectMetadataMap: ObjectMetadataMap;
|
||||||
|
}> {
|
||||||
|
let latestWorkspaceMetadataVersion =
|
||||||
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
|
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
|
||||||
|
|
||||||
if (latestWorkspaceMetadataVersion === undefined) {
|
if (latestWorkspaceMetadataVersion === undefined) {
|
||||||
await this.workspaceMetadataCacheService.recomputeMetadataCache(
|
await this.workspaceMetadataCacheService.recomputeMetadataCache({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
ignoreLock: !failOnMetadataCacheMiss,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (failOnMetadataCacheMiss) {
|
||||||
|
throw new TwentyORMException(
|
||||||
|
`Metadata version not found for workspace ${workspaceId}`,
|
||||||
|
TwentyORMExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
latestWorkspaceMetadataVersion =
|
||||||
|
await this.workspaceCacheStorageService.getMetadataVersion(
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!latestWorkspaceMetadataVersion) {
|
||||||
throw new TwentyORMException(
|
throw new TwentyORMException(
|
||||||
`Metadata version not found for workspace ${workspaceId}`,
|
`Metadata version not found after recompute for workspace ${workspaceId}`,
|
||||||
TwentyORMExceptionCode.METADATA_VERSION_NOT_FOUND,
|
TwentyORMExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const desiredWorkspaceMetadataVersion =
|
const objectMetadataMap =
|
||||||
workspaceMetadataVersion ?? latestWorkspaceMetadataVersion;
|
await this.workspaceCacheStorageService.getObjectMetadataMap(
|
||||||
|
workspaceId,
|
||||||
|
latestWorkspaceMetadataVersion,
|
||||||
|
);
|
||||||
|
|
||||||
if (latestWorkspaceMetadataVersion !== desiredWorkspaceMetadataVersion) {
|
if (!objectMetadataMap) {
|
||||||
throw new TwentyORMException(
|
throw new TwentyORMException(
|
||||||
`Workspace metadata version mismatch detected for workspace ${workspaceId}. Current version: ${latestWorkspaceMetadataVersion}. Desired version: ${desiredWorkspaceMetadataVersion}`,
|
`Object metadata map not found for workspace ${workspaceId}`,
|
||||||
TwentyORMExceptionCode.METADATA_VERSION_MISMATCH,
|
TwentyORMExceptionCode.METADATA_COLLECTION_NOT_FOUND,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return desiredWorkspaceMetadataVersion;
|
return {
|
||||||
}
|
metadataVersion: latestWorkspaceMetadataVersion,
|
||||||
|
objectMetadataMap,
|
||||||
public async destroy(
|
};
|
||||||
workspaceId: string,
|
|
||||||
metadataVersion: number | null,
|
|
||||||
): Promise<void> {
|
|
||||||
const desiredWorkspaceMetadataVersion =
|
|
||||||
this.computeDesiredWorkspaceMetadataVersion(workspaceId, metadataVersion);
|
|
||||||
|
|
||||||
await this.cacheManager.clearKey(
|
|
||||||
`${workspaceId}-${desiredWorkspaceMetadataVersion}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,16 +15,19 @@ export class TwentyORMGlobalManager {
|
|||||||
async getRepositoryForWorkspace<T extends ObjectLiteral>(
|
async getRepositoryForWorkspace<T extends ObjectLiteral>(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
workspaceEntity: Type<T>,
|
workspaceEntity: Type<T>,
|
||||||
|
failOnMetadataCacheMiss?: boolean,
|
||||||
): Promise<WorkspaceRepository<T>>;
|
): Promise<WorkspaceRepository<T>>;
|
||||||
|
|
||||||
async getRepositoryForWorkspace<T extends ObjectLiteral>(
|
async getRepositoryForWorkspace<T extends ObjectLiteral>(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
objectMetadataName: string,
|
objectMetadataName: string,
|
||||||
|
failOnMetadataCacheMiss?: boolean,
|
||||||
): Promise<WorkspaceRepository<T>>;
|
): Promise<WorkspaceRepository<T>>;
|
||||||
|
|
||||||
async getRepositoryForWorkspace<T extends ObjectLiteral>(
|
async getRepositoryForWorkspace<T extends ObjectLiteral>(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
workspaceEntityOrobjectMetadataName: Type<T> | string,
|
workspaceEntityOrobjectMetadataName: Type<T> | string,
|
||||||
|
failOnMetadataCacheMiss = true,
|
||||||
): Promise<WorkspaceRepository<T>> {
|
): Promise<WorkspaceRepository<T>> {
|
||||||
let objectMetadataName: string;
|
let objectMetadataName: string;
|
||||||
|
|
||||||
@ -39,6 +42,7 @@ export class TwentyORMGlobalManager {
|
|||||||
const workspaceDataSource = await this.workspaceDataSourceFactory.create(
|
const workspaceDataSource = await this.workspaceDataSourceFactory.create(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
null,
|
null,
|
||||||
|
failOnMetadataCacheMiss,
|
||||||
);
|
);
|
||||||
|
|
||||||
const repository = workspaceDataSource.getRepository<T>(objectMetadataName);
|
const repository = workspaceDataSource.getRepository<T>(objectMetadataName);
|
||||||
@ -50,11 +54,7 @@ export class TwentyORMGlobalManager {
|
|||||||
return await this.workspaceDataSourceFactory.create(workspaceId, null);
|
return await this.workspaceDataSourceFactory.create(workspaceId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadDataSourceForWorkspace(workspaceId: string) {
|
|
||||||
await this.workspaceDataSourceFactory.create(workspaceId, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
async destroyDataSourceForWorkspace(workspaceId: string) {
|
async destroyDataSourceForWorkspace(workspaceId: string) {
|
||||||
await this.workspaceDataSourceFactory.destroy(workspaceId, null);
|
await this.workspaceDataSourceFactory.destroy(workspaceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user