-
Notifications
You must be signed in to change notification settings - Fork 49
Open
Labels
investigationWhy is this happening?Why is this happening?solvedThe question was answered or the problem was solvedThe question was answered or the problem was solved
Description
I’m using a tenant-per-database setup and durable providers for TENANT_KYSELY_CONNECTION.
After upgrading @nestjs-cls/transactional from 2.5.1 to 2.6.0, I noticed an issue:
When making requests in the following sequence — Tenant A → Tenant B → Tenant A —
the second request for Tenant A incorrectly returns Tenant B’s connection (and data) 😅
Here’s the comparison between the versions:
@nestjs-cls/[email protected]...@nestjs-cls/[email protected]
I’ll try to create a reproducible example soon, but I’m not doing anything unusual.
Downgrading back to 2.5.1 resolves the issue.
A sample outline of the module. Everything in the registry is correct, it just the front txHost connection a service that uses it.
@Global()
@Module({
imports: [ClsModule],
providers: [TenantKyselyRegistry],
exports: [TenantKyselyRegistry],
})
export class TenantKyselyModule {
public static register(): DynamicModule {
return {
module: TenantKyselyModule,
imports: [
RequestConnectionModule,
VaultModule,
ConfigModule,
ClsModule.forRoot({
plugins: [
new ClsPluginTransactional({
imports: [],
adapter: new TransactionalAdapterKysely({
kyselyInstanceToken: TENANT_KYSELY_CONNECTION,
}),
}),
],
}),
],
providers: [
{
provide: TENANT_KYSELY_CONNECTION,
scope: Scope.REQUEST,
durable: true,
inject: [
REQUEST,
TenantKyselyRegistry,
ConfigService,
],
useFactory: ( request) => {
const tenantId = request.headers['tenant-id']
const { db } = registry.getOrCreate(tenantId, { database: tenantId , ....});
return db;
},
},
],
exports: [TENANT_KYSELY_CONNECTION],
};
}
}
@Injectable()
export class TenantKyselyRegistry implements OnApplicationShutdown {
private readonly registry = new Map<string, PoolEntry>();
private readonly logger = new Logger('TenantKyselyRegistry');
getOrCreate(tenantId: string, poolConfig: PoolConfig): PoolEntry {
const key = `${tenantId}`;
const existing = this.registry.get(key);
if (existing) return existing;
const pool = new Pool(poolConfig);
const db = new Kysely<unknown>({
dialect: new PostgresDialect({ pool }),
});
const entry = { pool, db } as const;
this.registry.set(key, entry);
return entry;
}
async onApplicationShutdown() {
const entries = Array.from(this.registry.values());
await Promise.allSettled(entries.map((e) => e.pool.end()));
this.registry.clear();
}
}
Metadata
Metadata
Assignees
Labels
investigationWhy is this happening?Why is this happening?solvedThe question was answered or the problem was solvedThe question was answered or the problem was solved