Read some endpoints on replica db (behind feature flag) (#16677)
Next steps: - move some workers' activities to replica db
This commit is contained in:
+1
@@ -214,6 +214,7 @@ describe('WorkspaceEntityManager', () => {
|
||||
IS_DASHBOARD_V2_ENABLED: false,
|
||||
IS_TIMELINE_ACTIVITY_MIGRATED: false,
|
||||
IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED: false,
|
||||
IS_READ_ON_REPLICA_ENABLED: false,
|
||||
},
|
||||
eventEmitterService: {
|
||||
emitMutationEvent: jest.fn(),
|
||||
|
||||
-2
@@ -1,7 +1,6 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
@@ -32,7 +31,6 @@ import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
WorkspaceFeatureFlagsMapCacheModule,
|
||||
FeatureFlagModule,
|
||||
TwentyConfigModule,
|
||||
WorkspaceEventEmitterModule,
|
||||
WorkspaceCacheModule,
|
||||
|
||||
+54
-4
@@ -1,10 +1,11 @@
|
||||
import {
|
||||
Injectable,
|
||||
Logger,
|
||||
OnApplicationShutdown,
|
||||
OnModuleInit,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
@@ -13,8 +14,9 @@ import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/worksp
|
||||
export class GlobalWorkspaceDataSourceService
|
||||
implements OnModuleInit, OnApplicationShutdown
|
||||
{
|
||||
private readonly logger = new Logger(GlobalWorkspaceDataSourceService.name);
|
||||
private globalWorkspaceDataSource: GlobalWorkspaceDataSource | null = null;
|
||||
private globalWorkspaceDataSourceReplica: GlobalWorkspaceDataSource | null =
|
||||
null;
|
||||
|
||||
constructor(
|
||||
private readonly twentyConfigService: TwentyConfigService,
|
||||
@@ -35,7 +37,9 @@ export class GlobalWorkspaceDataSourceService
|
||||
: undefined,
|
||||
poolSize: this.twentyConfigService.get('PG_POOL_MAX_CONNECTIONS'),
|
||||
extra: {
|
||||
query_timeout: 10000, // 10 seconds,
|
||||
query_timeout: this.twentyConfigService.get(
|
||||
'PG_DATABASE_PRIMARY_TIMEOUT_MS',
|
||||
),
|
||||
idleTimeoutMillis: this.twentyConfigService.get(
|
||||
'PG_POOL_IDLE_TIMEOUT_MS',
|
||||
),
|
||||
@@ -48,10 +52,44 @@ export class GlobalWorkspaceDataSourceService
|
||||
);
|
||||
|
||||
await this.globalWorkspaceDataSource.initialize();
|
||||
|
||||
const shouldInitializeReplicaDataSource = isDefined(
|
||||
this.twentyConfigService.get('PG_DATABASE_REPLICA_URL'),
|
||||
);
|
||||
|
||||
if (shouldInitializeReplicaDataSource) {
|
||||
this.globalWorkspaceDataSourceReplica = new GlobalWorkspaceDataSource(
|
||||
{
|
||||
url: this.twentyConfigService.get('PG_DATABASE_REPLICA_URL'),
|
||||
type: 'postgres',
|
||||
logging: this.twentyConfigService.getLoggingConfig(),
|
||||
entities: [],
|
||||
ssl: this.twentyConfigService.get('PG_SSL_ALLOW_SELF_SIGNED')
|
||||
? {
|
||||
rejectUnauthorized: false,
|
||||
}
|
||||
: undefined,
|
||||
poolSize: this.twentyConfigService.get('PG_POOL_MAX_CONNECTIONS'),
|
||||
extra: {
|
||||
query_timeout: this.twentyConfigService.get(
|
||||
'PG_DATABASE_REPLICA_TIMEOUT_MS',
|
||||
),
|
||||
idleTimeoutMillis: this.twentyConfigService.get(
|
||||
'PG_POOL_IDLE_TIMEOUT_MS',
|
||||
),
|
||||
allowExitOnIdle: this.twentyConfigService.get(
|
||||
'PG_POOL_ALLOW_EXIT_ON_IDLE',
|
||||
),
|
||||
},
|
||||
},
|
||||
this.workspaceEventEmitter,
|
||||
);
|
||||
await this.globalWorkspaceDataSourceReplica.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
public getGlobalWorkspaceDataSource(): GlobalWorkspaceDataSource {
|
||||
if (!this.globalWorkspaceDataSource) {
|
||||
if (!isDefined(this.globalWorkspaceDataSource)) {
|
||||
throw new Error(
|
||||
'GlobalWorkspaceDataSource has not been initialized. Make sure the module has been initialized.',
|
||||
);
|
||||
@@ -60,10 +98,22 @@ export class GlobalWorkspaceDataSourceService
|
||||
return this.globalWorkspaceDataSource;
|
||||
}
|
||||
|
||||
public getGlobalWorkspaceDataSourceReplica(): GlobalWorkspaceDataSource {
|
||||
if (!isDefined(this.globalWorkspaceDataSourceReplica)) {
|
||||
return this.getGlobalWorkspaceDataSource();
|
||||
}
|
||||
|
||||
return this.globalWorkspaceDataSourceReplica;
|
||||
}
|
||||
|
||||
async onApplicationShutdown(): Promise<void> {
|
||||
if (this.globalWorkspaceDataSource) {
|
||||
await this.globalWorkspaceDataSource.destroy();
|
||||
this.globalWorkspaceDataSource = null;
|
||||
}
|
||||
if (this.globalWorkspaceDataSourceReplica) {
|
||||
await this.globalWorkspaceDataSourceReplica.destroy();
|
||||
this.globalWorkspaceDataSourceReplica = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -4,6 +4,7 @@ import { type ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util';
|
||||
import { GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource';
|
||||
import { GlobalWorkspaceDataSourceService } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource.service';
|
||||
@@ -62,6 +63,10 @@ export class GlobalWorkspaceOrmManager {
|
||||
return this.globalWorkspaceDataSourceService.getGlobalWorkspaceDataSource();
|
||||
}
|
||||
|
||||
async getGlobalWorkspaceDataSourceReplica(): Promise<GlobalWorkspaceDataSource> {
|
||||
return this.globalWorkspaceDataSourceService.getGlobalWorkspaceDataSourceReplica();
|
||||
}
|
||||
|
||||
async executeInWorkspaceContext<T>(
|
||||
authContext: WorkspaceAuthContext,
|
||||
fn: () => T | Promise<T>,
|
||||
@@ -109,4 +114,13 @@ export class GlobalWorkspaceOrmManager {
|
||||
userWorkspaceRoleMap,
|
||||
};
|
||||
}
|
||||
|
||||
private async isReadOnReplicaEnabled(workspaceId: string): Promise<boolean> {
|
||||
const { featureFlagsMap } = await this.workspaceCacheService.getOrRecompute(
|
||||
workspaceId,
|
||||
['featureFlagsMap'],
|
||||
);
|
||||
|
||||
return !!featureFlagsMap[FeatureFlagKey.IS_READ_ON_REPLICA_ENABLED];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user