Fix pg pool implementation (#12106)

Fix the following error: 
Cannot use a pool after calling end on a pool

<img width="917" alt="Screenshot 2025-05-17 at 14 56 18"
src="https://github.com/user-attachments/assets/63081831-9a7e-4633-8274-de9f8a48dbae"
/>

The problem was that the datasource manager was destroying the
connections when a datasource cache expired.
This commit is contained in:
Félix Malfait
2025-05-17 15:22:10 +02:00
committed by GitHub
parent d93024fd02
commit 64d988cdec
2 changed files with 30 additions and 16 deletions

View File

@ -40,7 +40,7 @@ type CacheResult<T, U> = {
data: U;
};
const ONE_HOUR_IN_MS = 3600_000;
const TWENTY_MINUTES_IN_MS = 120_000;
@Injectable()
export class WorkspaceDatasourceFactory {
@ -58,6 +58,28 @@ export class WorkspaceDatasourceFactory {
private readonly workspaceFeatureFlagsMapCacheService: WorkspaceFeatureFlagsMapCacheService,
) {}
private async conditionalDestroyDataSource(
dataSource: WorkspaceDataSource,
): Promise<void> {
const isPoolSharingEnabled = this.twentyConfigService.get(
'PG_ENABLE_POOL_SHARING',
);
if (isPoolSharingEnabled) {
this.logger.debug(
`PromiseMemoizer Event: A WorkspaceDataSource (using shared pool) is being cleared. Actual pool closure managed by PgPoolSharedService. Not calling dataSource.destroy().`,
);
// We should NOT call dataSource.destroy() here, because that would end
// the shared pool, potentially affecting other active users of that pool.
// The PgPoolSharedService is responsible for the lifecycle of shared pools.
} else {
this.logger.debug(
`PromiseMemoizer Event: A WorkspaceDataSource (using dedicated pool) is being cleared. Calling safelyDestroyDataSource.`,
);
await this.safelyDestroyDataSource(dataSource);
}
}
private async safelyDestroyDataSource(
dataSource: WorkspaceDataSource,
): Promise<void> {
@ -208,7 +230,8 @@ export class WorkspaceDatasourceFactory {
// https://node-postgres.com/apis/pool
// TypeORM doesn't allow sharing connection pools between data sources
// So we keep a small pool open for longer if connection pooling patch isn't enabled
idleTimeoutMillis: ONE_HOUR_IN_MS,
// TODO: Probably not needed anymore when connection pooling patch is enabled
idleTimeoutMillis: TWENTY_MINUTES_IN_MS,
max: 4,
allowExitOnIdle: true,
},
@ -223,9 +246,7 @@ export class WorkspaceDatasourceFactory {
return workspaceDataSource;
},
async (dataSource) => {
await this.safelyDestroyDataSource(dataSource);
},
this.conditionalDestroyDataSource.bind(this),
);
if (!workspaceDataSource) {
@ -377,9 +398,7 @@ export class WorkspaceDatasourceFactory {
try {
await this.promiseMemoizer.clearKeys(
`${workspaceId}-`,
async (dataSource) => {
await this.safelyDestroyDataSource(dataSource);
},
this.conditionalDestroyDataSource.bind(this),
);
} catch (error) {
// Log and swallow any errors during cleanup to prevent crashes

View File

@ -20,11 +20,7 @@ interface PoolWithEndTracker extends Pool {
}
interface ExtendedPoolConfig extends PoolConfig {
extra?: {
allowExitOnIdle?: boolean;
idleTimeoutMillis?: number;
[key: string]: unknown;
};
allowExitOnIdle?: boolean;
}
interface PoolInternalStats {
@ -367,10 +363,9 @@ export class PgPoolSharedService {
poolConfig.idleTimeoutMillis = idleTimeoutMs;
}
if (!poolConfig.extra) {
poolConfig.extra = {};
if (allowExitOnIdle) {
poolConfig.allowExitOnIdle = allowExitOnIdle;
}
poolConfig.extra.allowExitOnIdle = allowExitOnIdle;
const key = buildPoolKey(poolConfig);
const existing = poolsMap.get(key);