mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
feat: Add query timeout support for MySql (#10846)
Add "enableQueryTimeout" option to MysqlConnectionOptions. When enabled the value of "maxQueryExecutionTime" will be passed to mysql driver as query timeout. --------- Co-authored-by: Mike Guida <mike@mguida.com>
This commit is contained in:
parent
45577df8b7
commit
046aebe696
@ -162,6 +162,11 @@ Different RDBMS-es have their own specific options.
|
||||
- `ssl` - object with SSL parameters or a string containing the name of the SSL profile.
|
||||
See [SSL options](https://github.com/mysqljs/mysql#ssl-options).
|
||||
|
||||
- `enableQueryTimeout` - If a value is specified for maxQueryExecutionTime, in addition to generating a warning log when a
|
||||
query exceeds this time limit,
|
||||
the specified maxQueryExecutionTime value is also used as the timeout for the query.
|
||||
For more information, check https://github.com/mysqljs/mysql?tab=readme-ov-file#timeouts
|
||||
|
||||
## `postgres` / `cockroachdb` data source options
|
||||
|
||||
- `url` - Connection url where the connection is performed. Please note that other data source options will override parameters set from url.
|
||||
@ -242,6 +247,7 @@ Different RDBMS-es have their own specific options.
|
||||
- `database` - Database name
|
||||
|
||||
## `mssql` data source options
|
||||
|
||||
Based on [tedious](https://tediousjs.github.io/node-mssql/) MSSQL implementation. See [SqlServerConnectionOptions.ts](..\src\driver\sqlserver\SqlServerConnectionOptions.ts) for details on exposed attributes.
|
||||
|
||||
- `url` - Connection url where the connection is performed. Please note that other data source options will override parameters set from url.
|
||||
@ -547,10 +553,10 @@ The following TNS connection string will be used in the next explanations:
|
||||
(SERVER=shared)))
|
||||
)
|
||||
```
|
||||
|
||||
- `sid` - The System Identifier (SID) identifies a specific database instance. For example, "sales".
|
||||
- `serviceName` - The Service Name is an identifier of a database service. For example, `sales.us.example.com`.
|
||||
|
||||
|
||||
## Data Source Options example
|
||||
|
||||
Here is a small example of data source options for mysql:
|
||||
|
||||
@ -109,6 +109,13 @@ export interface MysqlConnectionOptions
|
||||
*/
|
||||
readonly connectorPackage?: "mysql" | "mysql2"
|
||||
|
||||
/**
|
||||
* If a value is specified for maxQueryExecutionTime, in addition to generating a warning log when a query exceeds this time limit,
|
||||
* the specified maxQueryExecutionTime value is also used as the timeout for the query.
|
||||
* For more information, check https://github.com/mysqljs/mysql?tab=readme-ov-file#timeouts
|
||||
*/
|
||||
readonly enableQueryTimeout?: boolean
|
||||
|
||||
/**
|
||||
* Replication setup.
|
||||
*/
|
||||
|
||||
@ -199,10 +199,17 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
|
||||
query,
|
||||
parameters,
|
||||
)
|
||||
|
||||
const enableQueryTimeout =
|
||||
this.driver.options.enableQueryTimeout
|
||||
const maxQueryExecutionTime =
|
||||
this.driver.options.maxQueryExecutionTime
|
||||
const queryPayload =
|
||||
enableQueryTimeout && maxQueryExecutionTime
|
||||
? { sql: query, timeout: maxQueryExecutionTime }
|
||||
: query
|
||||
const queryStartTime = +new Date()
|
||||
databaseConnection.query(
|
||||
query,
|
||||
queryPayload,
|
||||
parameters,
|
||||
async (err: any, raw: any) => {
|
||||
// log slow queries if maxQueryExecution time is set
|
||||
|
||||
@ -0,0 +1,106 @@
|
||||
import { expect } from "chai"
|
||||
import "reflect-metadata"
|
||||
import { DataSource } from "../../../../../src"
|
||||
import {
|
||||
TestingOptions,
|
||||
closeTestingConnections,
|
||||
createTestingConnections,
|
||||
reloadTestingDatabases,
|
||||
} from "../../../../utils/test-utils"
|
||||
|
||||
describe("mysql driver > enableQueryTimeout connection option", () => {
|
||||
let dataSources: DataSource[]
|
||||
const commonConnectionOptions: TestingOptions = {
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchema: true,
|
||||
enabledDrivers: ["mysql"],
|
||||
}
|
||||
const timeoutMs = 10
|
||||
const longQueryTimeSec = 0.02
|
||||
const shortQueryTimeSec = 0.005
|
||||
|
||||
describe("when enableQueryTimeout is true", () => {
|
||||
before(async () => {
|
||||
dataSources = await createTestingConnections({
|
||||
...commonConnectionOptions,
|
||||
driverSpecific: {
|
||||
enableQueryTimeout: true,
|
||||
maxQueryExecutionTime: timeoutMs,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(() => reloadTestingDatabases(dataSources))
|
||||
after(() => closeTestingConnections(dataSources))
|
||||
|
||||
it("should throw a query execution timeout error for the query when it exceeds the maxQueryExecutionTime", async () => {
|
||||
await Promise.all(
|
||||
dataSources.map(async (dataSource) => {
|
||||
let errorThrown = false
|
||||
try {
|
||||
await dataSource.manager.query(
|
||||
`SELECT SLEEP(${longQueryTimeSec})`,
|
||||
)
|
||||
} catch (err) {
|
||||
errorThrown = true
|
||||
expect(err).to.have.nested.property(
|
||||
"driverError.code",
|
||||
"PROTOCOL_SEQUENCE_TIMEOUT",
|
||||
)
|
||||
expect(err).to.have.nested.property(
|
||||
"driverError.timeout",
|
||||
timeoutMs,
|
||||
)
|
||||
}
|
||||
expect(errorThrown).to.be.true
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should not throw a query execution timeout error for the query when it runs within the maxQueryExecutionTime", async () => {
|
||||
await Promise.all(
|
||||
dataSources.map(async (dataSource) => {
|
||||
let errorThrown = false
|
||||
try {
|
||||
await dataSource.manager.query(
|
||||
`SELECT SLEEP(${shortQueryTimeSec})`,
|
||||
)
|
||||
} catch (err) {
|
||||
errorThrown = true
|
||||
}
|
||||
expect(errorThrown).to.be.false
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("when enableQueryTimeout is not provided", () => {
|
||||
let datasources: DataSource[]
|
||||
|
||||
before(async () => {
|
||||
datasources = await createTestingConnections({
|
||||
...commonConnectionOptions,
|
||||
driverSpecific: { maxQueryExecutionTime: timeoutMs },
|
||||
})
|
||||
})
|
||||
|
||||
after(() => closeTestingConnections(datasources))
|
||||
|
||||
it("should not throw a query execution timeout error", () => {
|
||||
Promise.all(
|
||||
datasources.map(async (dataSource) => {
|
||||
let errorThrown = false
|
||||
try {
|
||||
await dataSource.manager.query(
|
||||
`SELECT SLEEP(${longQueryTimeSec})`,
|
||||
)
|
||||
} catch (err) {
|
||||
errorThrown = true
|
||||
}
|
||||
expect(errorThrown).to.be.false
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user