Describe the bug
In packages/components/nodes/recordmanager/MySQLRecordManager/MySQLRecordManager.ts, there is a critical anti-pattern regarding database connection management.
The getDataSource() method instantiates a completely new DataSource and executes .initialize() every time it is called. Subsequently, in all routine CRUD operations (e.g., update, exists, listKeys, createSchema, getTime), the code acquires this brand-new connection pool, executes the query, and then immediately destroys it via await dataSource.destroy() in the finally block.
This means that for every single read or write operation, the system undergoes a full, heavy process: MySQL TCP handshake, authentication, and connection pool initialization/destruction. Under high-concurrency scenarios (such as multiple agents concurrently reading/writing records, or upserting large documents with hundreds of chunks), this will trigger a massive surge in temporary connections. This behavior will quickly lead to network Port Exhaustion on the Node.js host or hit the Max Connections limit on the MySQL server, ultimately resulting in severe performance degradation, system unresponsiveness, or a Denial of Service (DoS) due to connection timeouts.
To Reproduce
Because this is an underlying architectural logic flaw, it can be conceptually reproduced by the following steps:
-
Go to your flow setup and configure the MySQL Record Manager.
-
Trigger a high-concurrency document vectorization or state persistence task (e.g., passing a complex document split into hundreds of chunks for upsertion).
-
Monitor the Node.js host's network port allocation or the new connection QPS on the MySQL server side.
-
See error: As concurrency increases, database response times will increase exponentially, eventually throwing connection timeouts or "too many connections / no available ports" errors.
Expected behavior
The DataSource should be maintained as a global or instance-level singleton. It should be initialized once (e.g., during the init() phase of the MySQLRecordManager) to establish a persistent, long-lived connection pool.
Subsequent methods executing database operations should reuse this central pool, strictly using queryRunner.release() to return individual cursors. The await dataSource.destroy() method should never be called after routine read/write operations.
Screenshots
N/A - This is an underlying code logic defect
Flow
N/A
Use Method
Docker
Flowise Version
Latest
Operating System
macOS
Browser
Chrome
Additional context
Core Defect Code Snippets:
The getDataSource() implementation causes a new instance on every call:
private async getDataSource(): Promise {
const { mysqlOptions } = this.config
// ...
const dataSource = new DataSource(mysqlOptions) // Created every time
await dataSource.initialize() // Heavy connection process repeats every time
return dataSource
}
Frequent disconnections in routine methods like update or exists:
const dataSource = await this.getDataSource() // Retrieves a brand-new temporary pool
try {
const queryRunner = dataSource.createQueryRunner()
// ... executing business SQL ...
await queryRunner.release()
} finally {
await dataSource.destroy() // Physically destroys the newly created pool
}
Suggested Fix: Elevate the DataSource instance to a class property of MySQLRecordManager for reuse, completely discarding the "one connection per operation" approach.
Describe the bug
In packages/components/nodes/recordmanager/MySQLRecordManager/MySQLRecordManager.ts, there is a critical anti-pattern regarding database connection management.
The getDataSource() method instantiates a completely new DataSource and executes .initialize() every time it is called. Subsequently, in all routine CRUD operations (e.g., update, exists, listKeys, createSchema, getTime), the code acquires this brand-new connection pool, executes the query, and then immediately destroys it via await dataSource.destroy() in the finally block.
This means that for every single read or write operation, the system undergoes a full, heavy process: MySQL TCP handshake, authentication, and connection pool initialization/destruction. Under high-concurrency scenarios (such as multiple agents concurrently reading/writing records, or upserting large documents with hundreds of chunks), this will trigger a massive surge in temporary connections. This behavior will quickly lead to network Port Exhaustion on the Node.js host or hit the Max Connections limit on the MySQL server, ultimately resulting in severe performance degradation, system unresponsiveness, or a Denial of Service (DoS) due to connection timeouts.
To Reproduce
Because this is an underlying architectural logic flaw, it can be conceptually reproduced by the following steps:
Go to your flow setup and configure the MySQL Record Manager.
Trigger a high-concurrency document vectorization or state persistence task (e.g., passing a complex document split into hundreds of chunks for upsertion).
Monitor the Node.js host's network port allocation or the new connection QPS on the MySQL server side.
See error: As concurrency increases, database response times will increase exponentially, eventually throwing connection timeouts or "too many connections / no available ports" errors.
Expected behavior
The DataSource should be maintained as a global or instance-level singleton. It should be initialized once (e.g., during the init() phase of the MySQLRecordManager) to establish a persistent, long-lived connection pool.
Subsequent methods executing database operations should reuse this central pool, strictly using queryRunner.release() to return individual cursors. The await dataSource.destroy() method should never be called after routine read/write operations.
Screenshots
N/A - This is an underlying code logic defect
Flow
N/A
Use Method
Docker
Flowise Version
Latest
Operating System
macOS
Browser
Chrome
Additional context
Core Defect Code Snippets:
The getDataSource() implementation causes a new instance on every call:
private async getDataSource(): Promise {
const { mysqlOptions } = this.config
// ...
const dataSource = new DataSource(mysqlOptions) // Created every time
await dataSource.initialize() // Heavy connection process repeats every time
return dataSource
}
Frequent disconnections in routine methods like update or exists:
const dataSource = await this.getDataSource() // Retrieves a brand-new temporary pool
try {
const queryRunner = dataSource.createQueryRunner()
// ... executing business SQL ...
await queryRunner.release()
} finally {
await dataSource.destroy() // Physically destroys the newly created pool
}
Suggested Fix: Elevate the DataSource instance to a class property of MySQLRecordManager for reuse, completely discarding the "one connection per operation" approach.