feat: add memory cache to boost performance (#2620)
* feat: add memory cache to boost performance * fix: tests * fix: logging * fix: missing commented stuff
This commit is contained in:
@ -0,0 +1,11 @@
|
||||
export class WorkspaceMigrationAppliedEvent {
|
||||
private readonly _workspaceId: string;
|
||||
|
||||
constructor(worskapceId: string) {
|
||||
this._workspaceId = worskapceId;
|
||||
}
|
||||
|
||||
get workspaceId(): string {
|
||||
return this._workspaceId;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
export enum WorkspaceMigrationEvents {
|
||||
MigrationApplied = '@workspace/migration-applied',
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
|
||||
import {
|
||||
QueryRunner,
|
||||
@ -17,6 +18,8 @@ import {
|
||||
WorkspaceMigrationColumnCreate,
|
||||
WorkspaceMigrationColumnRelation,
|
||||
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceMigrationEvents } from 'src/workspace/workspace-migration-runner/events/workspace-migration-events';
|
||||
import { WorkspaceMigrationAppliedEvent } from 'src/workspace/workspace-migration-runner/events/workspace-migration-applied.event';
|
||||
|
||||
import { customTableDefaultColumns } from './utils/custom-table-default-column.util';
|
||||
|
||||
@ -25,6 +28,7 @@ export class WorkspaceMigrationRunnerService {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -78,6 +82,12 @@ export class WorkspaceMigrationRunnerService {
|
||||
|
||||
await queryRunner.release();
|
||||
|
||||
// Emit event when migration is applied
|
||||
this.eventEmitter.emit(
|
||||
WorkspaceMigrationEvents.MigrationApplied,
|
||||
new WorkspaceMigrationAppliedEvent(workspaceId),
|
||||
);
|
||||
|
||||
return flattenedPendingMigrations;
|
||||
}
|
||||
|
||||
|
||||
@ -142,13 +142,9 @@ export class WorkspaceQueryRunnerService {
|
||||
)};
|
||||
`);
|
||||
|
||||
const queryFormatted = query
|
||||
.replace('neq:null', 'is:NOT_NULL')
|
||||
.replace('eq:null', 'is:NULL');
|
||||
|
||||
const results = await workspaceDataSource?.query<PGGraphQLResult>(`
|
||||
SELECT graphql.resolve($$
|
||||
${queryFormatted}
|
||||
${query}
|
||||
$$);
|
||||
`);
|
||||
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { MemoryStorageType } from 'src/integrations/environment/interfaces/memory-storage.interface';
|
||||
|
||||
import { MemoryStorageModule } from 'src/integrations/memory-storage/memory-storage.module';
|
||||
import { MemoryStorageJsonSerializer } from 'src/integrations/memory-storage/serializers/json.serializer';
|
||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module';
|
||||
import { WorkspaceSchemaStorageService } from 'src/workspace/workspace-schema-storage/workspace-schema-storage.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ObjectMetadataModule,
|
||||
MemoryStorageModule.forRoot({
|
||||
identifier: 'objectMetadataCollection',
|
||||
type: MemoryStorageType.Local,
|
||||
options: {},
|
||||
serializer: new MemoryStorageJsonSerializer<ObjectMetadataEntity[]>(),
|
||||
}),
|
||||
MemoryStorageModule.forRoot({
|
||||
identifier: 'typeDefs',
|
||||
type: MemoryStorageType.Local,
|
||||
options: {},
|
||||
}),
|
||||
],
|
||||
providers: [WorkspaceSchemaStorageService],
|
||||
exports: [WorkspaceSchemaStorageService],
|
||||
})
|
||||
export class WorkspaceSchemaStorageModule {}
|
||||
@ -0,0 +1,60 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
|
||||
import { InjectMemoryStorage } from 'src/integrations/memory-storage/decorators/inject-memory-storage.decorator';
|
||||
import { MemoryStorageService } from 'src/integrations/memory-storage/memory-storage.service';
|
||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceMigrationAppliedEvent } from 'src/workspace/workspace-migration-runner/events/workspace-migration-applied.event';
|
||||
import { WorkspaceMigrationEvents } from 'src/workspace/workspace-migration-runner/events/workspace-migration-events';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceSchemaStorageService {
|
||||
constructor(
|
||||
@InjectMemoryStorage('objectMetadataCollection')
|
||||
private readonly objectMetadataMemoryStorageService: MemoryStorageService<
|
||||
ObjectMetadataEntity[]
|
||||
>,
|
||||
@InjectMemoryStorage('typeDefs')
|
||||
private readonly typeDefsMemoryStorageService: MemoryStorageService<string>,
|
||||
) {}
|
||||
|
||||
setObjectMetadata(
|
||||
workspaceId: string,
|
||||
objectMetadata: ObjectMetadataEntity[],
|
||||
) {
|
||||
return this.objectMetadataMemoryStorageService.write({
|
||||
key: workspaceId,
|
||||
data: objectMetadata,
|
||||
});
|
||||
}
|
||||
|
||||
getObjectMetadata(
|
||||
workspaceId: string,
|
||||
): Promise<ObjectMetadataEntity[] | null> {
|
||||
return this.objectMetadataMemoryStorageService.read({
|
||||
key: workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
setTypeDefs(workspaceId: string, typeDefs: string): Promise<void> {
|
||||
return this.typeDefsMemoryStorageService.write({
|
||||
key: workspaceId,
|
||||
data: typeDefs,
|
||||
});
|
||||
}
|
||||
|
||||
getTypeDefs(workspaceId: string): Promise<string | null> {
|
||||
return this.typeDefsMemoryStorageService.read({
|
||||
key: workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the workspace schema storage when new migrations are applied for a specific workspace
|
||||
*/
|
||||
@OnEvent(WorkspaceMigrationEvents.MigrationApplied)
|
||||
handleMigrationAppliedEvent({ workspaceId }: WorkspaceMigrationAppliedEvent) {
|
||||
this.objectMetadataMemoryStorageService.delete({ key: workspaceId });
|
||||
this.typeDefsMemoryStorageService.delete({ key: workspaceId });
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
||||
import { WorkspaceSchemaStorageService } from 'src/workspace/workspace-schema-storage/workspace-schema-storage.service';
|
||||
|
||||
import { WorkspaceFactory } from './workspace.factory';
|
||||
|
||||
@ -31,6 +32,10 @@ describe('WorkspaceFactory', () => {
|
||||
provide: WorkspaceResolverFactory,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: WorkspaceSchemaStorageService,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import { makeExecutableSchema } from '@graphql-tools/schema';
|
||||
import { gql } from 'graphql-tag';
|
||||
|
||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||
import { WorkspaceSchemaStorageService } from 'src/workspace/workspace-schema-storage/workspace-schema-storage.service';
|
||||
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
||||
|
||||
import { WorkspaceGraphQLSchemaFactory } from './workspace-schema-builder/workspace-graphql-schema.factory';
|
||||
@ -18,6 +19,7 @@ export class WorkspaceFactory {
|
||||
private readonly objectMetadataService: ObjectMetadataService,
|
||||
private readonly workspaceGraphQLSchemaFactory: WorkspaceGraphQLSchemaFactory,
|
||||
private readonly workspaceResolverFactory: WorkspaceResolverFactory,
|
||||
private readonly workspaceSchemaStorageService: WorkspaceSchemaStorageService,
|
||||
) {}
|
||||
|
||||
async createGraphQLSchema(
|
||||
@ -37,15 +39,43 @@ export class WorkspaceFactory {
|
||||
return new GraphQLSchema({});
|
||||
}
|
||||
|
||||
const objectMetadataCollection =
|
||||
await this.objectMetadataService.getObjectMetadataFromWorkspaceId(
|
||||
workspaceId,
|
||||
);
|
||||
// Get object metadata from cache
|
||||
let objectMetadataCollection =
|
||||
await this.workspaceSchemaStorageService.getObjectMetadata(workspaceId);
|
||||
|
||||
const autoGeneratedSchema = await this.workspaceGraphQLSchemaFactory.create(
|
||||
objectMetadataCollection,
|
||||
workspaceResolverBuilderMethodNames,
|
||||
// If object metadata is not cached, get it from the database
|
||||
if (!objectMetadataCollection) {
|
||||
objectMetadataCollection =
|
||||
await this.objectMetadataService.getObjectMetadataFromWorkspaceId(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await this.workspaceSchemaStorageService.setObjectMetadata(
|
||||
workspaceId,
|
||||
objectMetadataCollection,
|
||||
);
|
||||
}
|
||||
|
||||
// Get typeDefs from cache
|
||||
let typeDefs = await this.workspaceSchemaStorageService.getTypeDefs(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
// If typeDefs are not cached, generate them
|
||||
if (!typeDefs) {
|
||||
const autoGeneratedSchema =
|
||||
await this.workspaceGraphQLSchemaFactory.create(
|
||||
objectMetadataCollection,
|
||||
workspaceResolverBuilderMethodNames,
|
||||
);
|
||||
typeDefs = printSchema(autoGeneratedSchema);
|
||||
|
||||
await this.workspaceSchemaStorageService.setTypeDefs(
|
||||
workspaceId,
|
||||
typeDefs,
|
||||
);
|
||||
}
|
||||
|
||||
const autoGeneratedResolvers = await this.workspaceResolverFactory.create(
|
||||
workspaceId,
|
||||
objectMetadataCollection,
|
||||
@ -53,7 +83,6 @@ export class WorkspaceFactory {
|
||||
);
|
||||
|
||||
// TODO: Cache the generate type definitions
|
||||
const typeDefs = printSchema(autoGeneratedSchema);
|
||||
const executableSchema = makeExecutableSchema({
|
||||
typeDefs: gql`
|
||||
${typeDefs}
|
||||
|
||||
@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { MetadataModule } from 'src/metadata/metadata.module';
|
||||
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
|
||||
import { WorkspaceSchemaStorageModule } from 'src/workspace/workspace-schema-storage/workspace-schema-storage.module';
|
||||
import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module';
|
||||
|
||||
import { WorkspaceFactory } from './workspace.factory';
|
||||
@ -16,6 +17,7 @@ import { WorkspaceResolverBuilderModule } from './workspace-resolver-builder/wor
|
||||
ObjectMetadataModule,
|
||||
WorkspaceSchemaBuilderModule,
|
||||
WorkspaceResolverBuilderModule,
|
||||
WorkspaceSchemaStorageModule,
|
||||
],
|
||||
providers: [WorkspaceFactory],
|
||||
exports: [WorkspaceFactory],
|
||||
|
||||
Reference in New Issue
Block a user