forge-sql-orm 2.1.10 → 2.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +202 -254
- package/dist/ForgeSQLORM.js +3238 -3227
- package/dist/ForgeSQLORM.js.map +1 -1
- package/dist/ForgeSQLORM.mjs +3236 -3225
- package/dist/ForgeSQLORM.mjs.map +1 -1
- package/dist/core/ForgeSQLORM.d.ts +65 -11
- package/dist/core/ForgeSQLORM.d.ts.map +1 -1
- package/dist/core/ForgeSQLQueryBuilder.d.ts +66 -11
- package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
- package/dist/core/SystemTables.d.ts +82 -82
- package/dist/utils/cacheUtils.d.ts.map +1 -1
- package/dist/utils/forgeDriver.d.ts.map +1 -1
- package/dist/utils/forgeDriverProxy.d.ts +6 -2
- package/dist/utils/forgeDriverProxy.d.ts.map +1 -1
- package/dist/utils/metadataContextUtils.d.ts +5 -2
- package/dist/utils/metadataContextUtils.d.ts.map +1 -1
- package/dist/utils/sqlUtils.d.ts +72 -1
- package/dist/utils/sqlUtils.d.ts.map +1 -1
- package/dist/webtriggers/index.d.ts +1 -1
- package/dist/webtriggers/index.d.ts.map +1 -1
- package/dist/webtriggers/slowQuerySchedulerTrigger.d.ts +67 -0
- package/dist/webtriggers/slowQuerySchedulerTrigger.d.ts.map +1 -0
- package/package.json +10 -10
- package/src/core/ForgeSQLORM.ts +164 -33
- package/src/core/ForgeSQLQueryBuilder.ts +65 -11
- package/src/core/SystemTables.ts +1 -1
- package/src/utils/cacheUtils.ts +3 -1
- package/src/utils/forgeDriver.ts +7 -34
- package/src/utils/forgeDriverProxy.ts +58 -6
- package/src/utils/metadataContextUtils.ts +21 -6
- package/src/utils/sqlUtils.ts +229 -2
- package/src/webtriggers/index.ts +1 -1
- package/src/webtriggers/slowQuerySchedulerTrigger.ts +82 -0
- package/dist/webtriggers/topSlowestStatementLastHourTrigger.d.ts +0 -114
- package/dist/webtriggers/topSlowestStatementLastHourTrigger.d.ts.map +0 -1
- package/src/webtriggers/topSlowestStatementLastHourTrigger.ts +0 -563
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cacheUtils.d.ts","sourceRoot":"","sources":["../../src/utils/cacheUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AA2ClE;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAK5C;AAmKD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,aAAa,EACtD,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAoBf;AACD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBlF;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,KAAK,CAAA;CAAE,EAC7B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"cacheUtils.d.ts","sourceRoot":"","sources":["../../src/utils/cacheUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AA2ClE;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAK5C;AAmKD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,aAAa,EACtD,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAoBf;AACD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBlF;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,KAAK,CAAA;CAAE,EAC7B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CA6CxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,KAAK,CAAA;CAAE,EAC7B,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA8Cf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forgeDriver.d.ts","sourceRoot":"","sources":["../../src/utils/forgeDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"forgeDriver.d.ts","sourceRoot":"","sources":["../../src/utils/forgeDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAQtD;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,EAAE,CAAC;CACL,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1D,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,CAAC;AAE5C;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,mBAAmB,CAO9E;AA6FD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,WAAW,GACtB,OAAO,MAAM,EACb,QAAQ,OAAO,EAAE,EACjB,QAAQ,WAAW,KAClB,OAAO,CAAC,iBAAiB,CAe3B,CAAC"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { SqlHints } from "./sqlHints";
|
|
2
|
+
import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
|
|
2
3
|
/**
|
|
3
|
-
* Creates a proxy for the forgeDriver that injects SQL hints
|
|
4
|
+
* Creates a proxy for the forgeDriver that injects SQL hints and handles query analysis
|
|
5
|
+
* @param forgeSqlOperation - The ForgeSQL operation instance
|
|
6
|
+
* @param options - SQL hints to inject
|
|
7
|
+
* @param logRawSqlQuery - Whether to log raw SQL queries
|
|
4
8
|
* @returns A proxied version of the forgeDriver
|
|
5
9
|
*/
|
|
6
|
-
export declare function createForgeDriverProxy(options?: SqlHints, logRawSqlQuery?: boolean): (query: string, params: any[], method: "all" | "execute") => Promise<{
|
|
10
|
+
export declare function createForgeDriverProxy(forgeSqlOperation: ForgeSqlOperation, options?: SqlHints, logRawSqlQuery?: boolean): (query: string, params: any[], method: "all" | "execute") => Promise<{
|
|
7
11
|
rows: any[];
|
|
8
12
|
insertId?: number;
|
|
9
13
|
affectedRows?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forgeDriverProxy.d.ts","sourceRoot":"","sources":["../../src/utils/forgeDriverProxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"forgeDriverProxy.d.ts","sourceRoot":"","sources":["../../src/utils/forgeDriverProxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAgBjE;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,iBAAiB,EAAE,iBAAiB,EACpC,OAAO,CAAC,EAAE,QAAQ,EAClB,cAAc,CAAC,EAAE,OAAO,IAGtB,OAAO,MAAM,EACb,QAAQ,GAAG,EAAE,EACb,QAAQ,KAAK,GAAG,SAAS,KACxB,OAAO,CAAC;IACT,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC,CAiDH"}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { ForgeSQLMetadata } from "./forgeDriver";
|
|
3
|
+
import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
|
|
3
4
|
export type MetadataQueryContext = {
|
|
4
5
|
totalDbExecutionTime: number;
|
|
5
6
|
totalResponseSize: number;
|
|
6
|
-
|
|
7
|
+
beginTime: Date;
|
|
8
|
+
printQueriesWithPlan: () => Promise<void>;
|
|
9
|
+
forgeSQLORM: ForgeSqlOperation;
|
|
7
10
|
};
|
|
8
11
|
export declare const metadataQueryContext: AsyncLocalStorage<MetadataQueryContext>;
|
|
9
|
-
export declare function saveMetaDataToContext(metadata
|
|
12
|
+
export declare function saveMetaDataToContext(metadata?: ForgeSQLMetadata): Promise<void>;
|
|
10
13
|
export declare function getLastestMetadata(): Promise<MetadataQueryContext | undefined>;
|
|
11
14
|
//# sourceMappingURL=metadataContextUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadataContextUtils.d.ts","sourceRoot":"","sources":["../../src/utils/metadataContextUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"metadataContextUtils.d.ts","sourceRoot":"","sources":["../../src/utils/metadataContextUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGjE,MAAM,MAAM,oBAAoB,GAAG;IACjC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,IAAI,CAAC;IAChB,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AACF,eAAO,MAAM,oBAAoB,yCAAgD,CAAC;AAElF,wBAAsB,qBAAqB,CAAC,QAAQ,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBtF;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAEpF"}
|
package/dist/utils/sqlUtils.d.ts
CHANGED
|
@@ -5,7 +5,11 @@ import { AnyIndexBuilder } from "drizzle-orm/mysql-core/indexes";
|
|
|
5
5
|
import { CheckBuilder } from "drizzle-orm/mysql-core/checks";
|
|
6
6
|
import { ForeignKeyBuilder } from "drizzle-orm/mysql-core/foreign-keys";
|
|
7
7
|
import { UniqueConstraintBuilder } from "drizzle-orm/mysql-core/unique-constraint";
|
|
8
|
-
import
|
|
8
|
+
import { SelectedFields } from "drizzle-orm/mysql-core/query-builders/select.types";
|
|
9
|
+
import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
|
|
10
|
+
import { ColumnDataType } from "drizzle-orm/column-builder";
|
|
11
|
+
import { AnyMySqlColumn } from "drizzle-orm/mysql-core/columns/common";
|
|
12
|
+
import type { ColumnBaseConfig } from "drizzle-orm/column";
|
|
9
13
|
/**
|
|
10
14
|
* Interface representing table metadata information
|
|
11
15
|
*/
|
|
@@ -77,5 +81,72 @@ export declare function mapSelectFieldsWithAlias<TSelection extends SelectedFiel
|
|
|
77
81
|
export declare function applyFromDriverTransform<T, TSelection>(rows: T[], selections: TSelection, aliasMap: Record<string, AnyColumn>): T[];
|
|
78
82
|
export declare function formatLimitOffset(limitOrOffset: number): number;
|
|
79
83
|
export declare function nextVal(sequenceName: string): number;
|
|
84
|
+
/**
|
|
85
|
+
* Analyzes and prints query performance data from CLUSTER_STATEMENTS_SUMMARY table.
|
|
86
|
+
*
|
|
87
|
+
* This function queries the CLUSTER_STATEMENTS_SUMMARY table to find queries that were executed
|
|
88
|
+
* within the specified time window and prints detailed performance information including:
|
|
89
|
+
* - SQL query text
|
|
90
|
+
* - Memory usage (average and max in MB)
|
|
91
|
+
* - Execution time (average in ms)
|
|
92
|
+
* - Number of executions
|
|
93
|
+
* - Execution plan
|
|
94
|
+
*
|
|
95
|
+
* @param forgeSQLORM - The ForgeSQL operation instance for database access
|
|
96
|
+
* @param timeDiffMs - Time window in milliseconds to look back for queries (e.g., 1500 for last 1.5 seconds)
|
|
97
|
+
* @param timeout - Optional timeout in milliseconds for the query execution (defaults to 1500ms)
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* // Analyze queries from the last 2 seconds
|
|
102
|
+
* await printQueriesWithPlan(forgeSQLORM, 2000);
|
|
103
|
+
*
|
|
104
|
+
* // Analyze queries with custom timeout
|
|
105
|
+
* await printQueriesWithPlan(forgeSQLORM, 1000, 3000);
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* @throws Does not throw - errors are logged to console.debug instead
|
|
109
|
+
*/
|
|
110
|
+
export declare function printQueriesWithPlan(forgeSQLORM: ForgeSqlOperation, timeDiffMs: number, timeout?: number): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Analyzes and logs slow queries from the last specified number of hours.
|
|
113
|
+
*
|
|
114
|
+
* This function queries the slow query system table to find queries that were executed
|
|
115
|
+
* within the specified time window and logs detailed performance information including:
|
|
116
|
+
* - SQL query text
|
|
117
|
+
* - Maximum memory usage (in MB)
|
|
118
|
+
* - Query execution time (in ms)
|
|
119
|
+
* - Execution count
|
|
120
|
+
* - Execution plan
|
|
121
|
+
*
|
|
122
|
+
* @param forgeSQLORM - The ForgeSQL operation instance for database access
|
|
123
|
+
* @param hours - Number of hours to look back for slow queries (e.g., 1 for last hour, 24 for last day)
|
|
124
|
+
* @param timeout - Optional timeout in milliseconds for the query execution (defaults to 1500ms)
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* // Analyze slow queries from the last hour
|
|
129
|
+
* await slowQueryPerHours(forgeSQLORM, 1);
|
|
130
|
+
*
|
|
131
|
+
* // Analyze slow queries from the last 24 hours with custom timeout
|
|
132
|
+
* await slowQueryPerHours(forgeSQLORM, 24, 3000);
|
|
133
|
+
*
|
|
134
|
+
* // Analyze slow queries from the last 6 hours
|
|
135
|
+
* await slowQueryPerHours(forgeSQLORM, 6);
|
|
136
|
+
* ```
|
|
137
|
+
*
|
|
138
|
+
* @throws Does not throw - errors are logged to console.debug instead
|
|
139
|
+
*/
|
|
140
|
+
export declare function slowQueryPerHours(forgeSQLORM: ForgeSqlOperation, hours: number, timeout?: number): Promise<string[]>;
|
|
141
|
+
/**
|
|
142
|
+
* Executes a promise with a timeout.
|
|
143
|
+
*
|
|
144
|
+
* @param promise - The promise to execute
|
|
145
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
146
|
+
* @returns Promise that resolves with the result or rejects on timeout
|
|
147
|
+
* @throws {Error} When the operation times out
|
|
148
|
+
*/
|
|
149
|
+
export declare function withTimeout<T>(promise: Promise<T>, message: string, timeoutMs: number): Promise<T>;
|
|
150
|
+
export declare function withTidbHint<TDataType extends ColumnDataType, TPartial extends Partial<ColumnBaseConfig<TDataType, string>>>(column: AnyMySqlColumn<TPartial>): AnyMySqlColumn<TPartial>;
|
|
80
151
|
export {};
|
|
81
152
|
//# sourceMappingURL=sqlUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlUtils.d.ts","sourceRoot":"","sources":["../../src/utils/sqlUtils.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"sqlUtils.d.ts","sourceRoot":"","sources":["../../src/utils/sqlUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ export * from "./applyMigrationsWebTrigger";
|
|
|
3
3
|
export * from "./fetchSchemaWebTrigger";
|
|
4
4
|
export * from "./dropTablesMigrationWebTrigger";
|
|
5
5
|
export * from "./clearCacheSchedulerTrigger";
|
|
6
|
-
export * from "./
|
|
6
|
+
export * from "./slowQuerySchedulerTrigger";
|
|
7
7
|
export interface TriggerResponse<BODY> {
|
|
8
8
|
body?: BODY;
|
|
9
9
|
headers?: Record<string, string[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webtriggers/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webtriggers/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAE5C,MAAM,WAAW,eAAe,CAAC,IAAI;IACnC,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,eAAe,GAAI,IAAI,EAAE,YAAY,MAAM,EAAE,MAAM,IAAI,KAAG,eAAe,CAAC,IAAI,CAc1F,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { TriggerResponse } from "./index";
|
|
2
|
+
import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
|
|
3
|
+
/**
|
|
4
|
+
* Scheduler trigger for analyzing slow queries from the last specified number of hours.
|
|
5
|
+
*
|
|
6
|
+
* This trigger analyzes slow queries from TiDB's slow query log system table and provides
|
|
7
|
+
* detailed performance information including SQL query text, memory usage, execution time,
|
|
8
|
+
* and execution plans. It's designed to be used as a scheduled trigger in Atlassian Forge
|
|
9
|
+
* to monitor query performance over time.
|
|
10
|
+
*
|
|
11
|
+
* The function queries the slow query system table to find queries executed within the
|
|
12
|
+
* specified time window and logs detailed performance information to the console. Results
|
|
13
|
+
* are limited to the top 50 slow queries to prevent excessive output.
|
|
14
|
+
*
|
|
15
|
+
* @param forgeSQLORM - The ForgeSQL operation instance for database access
|
|
16
|
+
* @param options - Configuration options for the slow query analysis
|
|
17
|
+
* @param options.hours - Number of hours to look back for slow queries (default: 1)
|
|
18
|
+
* @param options.timeout - Timeout in milliseconds for the query execution (default: 2000ms)
|
|
19
|
+
*
|
|
20
|
+
* @returns Promise<TriggerResponse<string>> - HTTP response with JSON stringified query results or error message
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import ForgeSQL, { slowQuerySchedulerTrigger } from "forge-sql-orm";
|
|
25
|
+
*
|
|
26
|
+
* const forgeSQL = new ForgeSQL();
|
|
27
|
+
*
|
|
28
|
+
* // Basic usage with default options (1 hour, 2000ms timeout)
|
|
29
|
+
* export const slowQueryTrigger = () =>
|
|
30
|
+
* slowQuerySchedulerTrigger(forgeSQL, { hours: 1, timeout: 2000 });
|
|
31
|
+
*
|
|
32
|
+
* // Analyze slow queries from the last 6 hours with extended timeout
|
|
33
|
+
* export const sixHourSlowQueryTrigger = () =>
|
|
34
|
+
* slowQuerySchedulerTrigger(forgeSQL, { hours: 6, timeout: 5000 });
|
|
35
|
+
*
|
|
36
|
+
* // Analyze slow queries from the last 24 hours
|
|
37
|
+
* export const dailySlowQueryTrigger = () =>
|
|
38
|
+
* slowQuerySchedulerTrigger(forgeSQL, { hours: 24, timeout: 3000 });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```yaml
|
|
43
|
+
* # manifest.yml configuration
|
|
44
|
+
* scheduledTrigger:
|
|
45
|
+
* - key: slow-query-trigger
|
|
46
|
+
* function: slowQueryTrigger
|
|
47
|
+
* interval: hour
|
|
48
|
+
*
|
|
49
|
+
* function:
|
|
50
|
+
* - key: slowQueryTrigger
|
|
51
|
+
* handler: index.slowQueryTrigger
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* - Results are automatically logged to the Forge Developer Console via `console.warn()`
|
|
56
|
+
* - The function returns up to 50 slow queries to prevent excessive logging
|
|
57
|
+
* - Transient timeouts are usually fine; repeated timeouts indicate the diagnostic query itself is slow
|
|
58
|
+
* - This trigger is best used with hourly intervals to catch slow queries in a timely manner
|
|
59
|
+
* - Error responses return HTTP 500 with error details
|
|
60
|
+
*
|
|
61
|
+
* @see {@link slowQueryPerHours} - The underlying function that performs the actual query analysis
|
|
62
|
+
*/
|
|
63
|
+
export declare function slowQuerySchedulerTrigger(forgeSQLORM: ForgeSqlOperation, options: {
|
|
64
|
+
hours: number;
|
|
65
|
+
timeout: number;
|
|
66
|
+
}): Promise<TriggerResponse<string>>;
|
|
67
|
+
//# sourceMappingURL=slowQuerySchedulerTrigger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slowQuerySchedulerTrigger.d.ts","sourceRoot":"","sources":["../../src/webtriggers/slowQuerySchedulerTrigger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,eAAe,EAAC,MAAM,SAAS,CAAC;AAEzD,OAAO,EAAC,iBAAiB,EAAC,MAAM,8BAA8B,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAsB,yBAAyB,CAAC,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE;IACrF,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAA;CAClB,GAAG,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAanC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forge-sql-orm",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.11",
|
|
4
4
|
"description": "Drizzle ORM integration for Atlassian @forge/sql. Provides a custom driver, schema migration, two levels of caching (local and global via @forge/kvs), optimistic locking, and query analysis.",
|
|
5
5
|
"main": "dist/ForgeSQLORM.js",
|
|
6
6
|
"module": "dist/ForgeSQLORM.mjs",
|
|
@@ -34,22 +34,22 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@eslint/js": "^9.38.0",
|
|
36
36
|
"@types/luxon": "^3.7.1",
|
|
37
|
-
"@types/node": "^24.9.
|
|
37
|
+
"@types/node": "^24.9.2",
|
|
38
38
|
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
|
39
39
|
"@typescript-eslint/parser": "^8.46.2",
|
|
40
|
-
"@vitest/coverage-v8": "^4.0.
|
|
41
|
-
"@vitest/ui": "^4.0.
|
|
40
|
+
"@vitest/coverage-v8": "^4.0.6",
|
|
41
|
+
"@vitest/ui": "^4.0.6",
|
|
42
42
|
"eslint": "^9.38.0",
|
|
43
43
|
"eslint-config-prettier": "^10.1.8",
|
|
44
44
|
"eslint-plugin-import": "^2.32.0",
|
|
45
45
|
"eslint-plugin-vitest": "^0.5.4",
|
|
46
|
-
"knip": "^5.66.
|
|
46
|
+
"knip": "^5.66.4",
|
|
47
47
|
"prettier": "^3.6.2",
|
|
48
48
|
"ts-node": "^10.9.2",
|
|
49
49
|
"typescript": "^5.9.3",
|
|
50
50
|
"uuid": "^13.0.0",
|
|
51
|
-
"vite": "^7.1.
|
|
52
|
-
"vitest": "^4.0.
|
|
51
|
+
"vite": "^7.1.12",
|
|
52
|
+
"vitest": "^4.0.6"
|
|
53
53
|
},
|
|
54
54
|
"license": "MIT",
|
|
55
55
|
"author": "Vasyl Zakharchenko",
|
|
@@ -76,11 +76,11 @@
|
|
|
76
76
|
"README.md"
|
|
77
77
|
],
|
|
78
78
|
"peerDependencies": {
|
|
79
|
-
"@forge/sql": "^3.0.
|
|
80
|
-
"drizzle-orm": "^0.44.
|
|
79
|
+
"@forge/sql": "^3.0.9",
|
|
80
|
+
"drizzle-orm": "^0.44.7"
|
|
81
81
|
},
|
|
82
82
|
"optionalDependencies": {
|
|
83
|
-
"@forge/kvs": "^1.0.
|
|
83
|
+
"@forge/kvs": "^1.0.8"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
86
|
"luxon": "^3.7.2"
|
package/src/core/ForgeSQLORM.ts
CHANGED
|
@@ -38,7 +38,6 @@ import { cacheApplicationContext, localCacheApplicationContext } from "../utils/
|
|
|
38
38
|
import { clearTablesCache } from "../utils/cacheUtils";
|
|
39
39
|
import { SQLWrapper } from "drizzle-orm/sql/sql";
|
|
40
40
|
import { WithSubquery } from "drizzle-orm/subquery";
|
|
41
|
-
import { ForgeSQLMetadata } from "../utils/forgeDriver";
|
|
42
41
|
import { getLastestMetadata, metadataQueryContext } from "../utils/metadataContextUtils";
|
|
43
42
|
import { operationTypeQueryContext } from "../utils/requestTypeContextUtils";
|
|
44
43
|
import type { MySqlQueryResultKind } from "drizzle-orm/mysql-core/session";
|
|
@@ -90,7 +89,11 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
|
|
|
90
89
|
console.debug("Initializing ForgeSQLORM...");
|
|
91
90
|
}
|
|
92
91
|
// Initialize Drizzle instance with our custom driver
|
|
93
|
-
const proxiedDriver = createForgeDriverProxy(
|
|
92
|
+
const proxiedDriver = createForgeDriverProxy(
|
|
93
|
+
this,
|
|
94
|
+
newOptions.hints,
|
|
95
|
+
newOptions.logRawSqlQuery,
|
|
96
|
+
);
|
|
94
97
|
this.drizzle = patchDbWithSelectAliased(
|
|
95
98
|
drizzle(proxiedDriver, { logger: newOptions.logRawSqlQuery }),
|
|
96
99
|
newOptions,
|
|
@@ -107,52 +110,125 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
|
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
/**
|
|
110
|
-
* Executes a query and provides access to execution metadata.
|
|
113
|
+
* Executes a query and provides access to execution metadata with performance monitoring.
|
|
111
114
|
* This method allows you to capture detailed information about query execution
|
|
112
|
-
* including database execution time, response size, and
|
|
115
|
+
* including database execution time, response size, and query analysis capabilities.
|
|
116
|
+
*
|
|
117
|
+
* The method aggregates metrics across all database operations within the query function,
|
|
118
|
+
* making it ideal for monitoring resolver performance and detecting performance issues.
|
|
113
119
|
*
|
|
114
120
|
* @template T - The return type of the query
|
|
115
|
-
* @param query - A function that returns a Promise with the query result
|
|
116
|
-
* @param onMetadata - Callback function that receives execution metadata
|
|
121
|
+
* @param query - A function that returns a Promise with the query result. Can contain multiple database operations.
|
|
122
|
+
* @param onMetadata - Callback function that receives aggregated execution metadata
|
|
123
|
+
* @param onMetadata.totalDbExecutionTime - Total database execution time across all operations in the query function (in milliseconds)
|
|
124
|
+
* @param onMetadata.totalResponseSize - Total response size across all operations (in bytes)
|
|
125
|
+
* @param onMetadata.printQueries - Function to analyze and print query execution plans from CLUSTER_STATEMENTS_SUMMARY
|
|
117
126
|
* @returns Promise with the query result
|
|
127
|
+
*
|
|
118
128
|
* @example
|
|
119
129
|
* ```typescript
|
|
130
|
+
* // Basic usage with performance monitoring
|
|
120
131
|
* const result = await forgeSQL.executeWithMetadata(
|
|
121
|
-
* async () =>
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
132
|
+
* async () => {
|
|
133
|
+
* const users = await forgeSQL.selectFrom(usersTable);
|
|
134
|
+
* const orders = await forgeSQL.selectFrom(ordersTable).where(eq(ordersTable.userId, usersTable.id));
|
|
135
|
+
* return { users, orders };
|
|
136
|
+
* },
|
|
137
|
+
* (totalDbExecutionTime, totalResponseSize, printQueries) => {
|
|
138
|
+
* const threshold = 500; // ms baseline for this resolver
|
|
139
|
+
*
|
|
140
|
+
* if (totalDbExecutionTime > threshold * 1.5) {
|
|
141
|
+
* console.warn(`[Performance Warning] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
|
|
142
|
+
* await printQueries(); // Analyze and print query execution plans
|
|
143
|
+
* } else if (totalDbExecutionTime > threshold) {
|
|
144
|
+
* console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
|
|
145
|
+
* }
|
|
146
|
+
*
|
|
147
|
+
* console.log(`DB response size: ${totalResponseSize} bytes`);
|
|
126
148
|
* }
|
|
127
149
|
* );
|
|
128
150
|
* ```
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* // Resolver with performance monitoring
|
|
155
|
+
* resolver.define("fetch", async (req: Request) => {
|
|
156
|
+
* try {
|
|
157
|
+
* return await forgeSQL.executeWithMetadata(
|
|
158
|
+
* async () => {
|
|
159
|
+
* // Resolver logic with multiple queries
|
|
160
|
+
* const users = await forgeSQL.selectFrom(demoUsers);
|
|
161
|
+
* const orders = await forgeSQL.selectFrom(demoOrders)
|
|
162
|
+
* .where(eq(demoOrders.userId, demoUsers.id));
|
|
163
|
+
* return { users, orders };
|
|
164
|
+
* },
|
|
165
|
+
* async (totalDbExecutionTime, totalResponseSize, printQueries) => {
|
|
166
|
+
* const threshold = 500; // ms baseline for this resolver
|
|
167
|
+
*
|
|
168
|
+
* if (totalDbExecutionTime > threshold * 1.5) {
|
|
169
|
+
* console.warn(`[Performance Warning fetch] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
|
|
170
|
+
* await printQueries(); // Optionally log or capture diagnostics for further analysis
|
|
171
|
+
* } else if (totalDbExecutionTime > threshold) {
|
|
172
|
+
* console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
|
|
173
|
+
* }
|
|
174
|
+
*
|
|
175
|
+
* console.log(`DB response size: ${totalResponseSize} bytes`);
|
|
176
|
+
* }
|
|
177
|
+
* );
|
|
178
|
+
* } catch (e) {
|
|
179
|
+
* const error = e?.cause?.debug?.sqlMessage ?? e?.cause;
|
|
180
|
+
* console.error(error, e);
|
|
181
|
+
* throw error;
|
|
182
|
+
* }
|
|
183
|
+
* });
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
186
|
+
* @note **Important**: When multiple resolvers are running concurrently, their query data may also appear in `printQueries()` analysis, as it queries the global `CLUSTER_STATEMENTS_SUMMARY` table.
|
|
129
187
|
*/
|
|
130
188
|
async executeWithMetadata<T>(
|
|
131
189
|
query: () => Promise<T>,
|
|
132
190
|
onMetadata: (
|
|
133
191
|
totalDbExecutionTime: number,
|
|
134
192
|
totalResponseSize: number,
|
|
135
|
-
|
|
193
|
+
printQueriesWithPlan: () => Promise<void>,
|
|
136
194
|
) => Promise<void> | void,
|
|
137
195
|
): Promise<T> {
|
|
138
196
|
return metadataQueryContext.run(
|
|
139
197
|
{
|
|
140
198
|
totalDbExecutionTime: 0,
|
|
141
199
|
totalResponseSize: 0,
|
|
200
|
+
beginTime: new Date(),
|
|
201
|
+
forgeSQLORM: this,
|
|
202
|
+
printQueriesWithPlan: async () => {
|
|
203
|
+
return;
|
|
204
|
+
},
|
|
142
205
|
},
|
|
143
206
|
async () => {
|
|
144
|
-
|
|
145
|
-
return await query();
|
|
146
|
-
} finally {
|
|
207
|
+
const result = await query();
|
|
147
208
|
const metadata = await getLastestMetadata();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
209
|
+
try {
|
|
210
|
+
if (metadata) {
|
|
211
|
+
await onMetadata(
|
|
212
|
+
metadata.totalDbExecutionTime,
|
|
213
|
+
metadata.totalResponseSize,
|
|
214
|
+
metadata.printQueriesWithPlan,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
} catch (e: any) {
|
|
218
|
+
// eslint-disable-next-line no-console
|
|
219
|
+
console.error(
|
|
220
|
+
"[ForgeSQLORM][executeWithMetadata] Failed to run onMetadata callback",
|
|
221
|
+
{
|
|
222
|
+
errorMessage: e?.message,
|
|
223
|
+
errorStack: e?.stack,
|
|
224
|
+
totalDbExecutionTime: metadata?.totalDbExecutionTime,
|
|
225
|
+
totalResponseSize: metadata?.totalResponseSize,
|
|
226
|
+
beginTime: metadata?.beginTime,
|
|
227
|
+
},
|
|
228
|
+
e,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
return result;
|
|
156
232
|
},
|
|
157
233
|
);
|
|
158
234
|
}
|
|
@@ -762,32 +838,87 @@ class ForgeSQLORM implements ForgeSqlOperation {
|
|
|
762
838
|
}
|
|
763
839
|
|
|
764
840
|
/**
|
|
765
|
-
* Executes a query and provides access to execution metadata.
|
|
841
|
+
* Executes a query and provides access to execution metadata with performance monitoring.
|
|
766
842
|
* This method allows you to capture detailed information about query execution
|
|
767
|
-
* including database execution time, response size, and
|
|
843
|
+
* including database execution time, response size, and query analysis capabilities.
|
|
844
|
+
*
|
|
845
|
+
* The method aggregates metrics across all database operations within the query function,
|
|
846
|
+
* making it ideal for monitoring resolver performance and detecting performance issues.
|
|
768
847
|
*
|
|
769
848
|
* @template T - The return type of the query
|
|
770
|
-
* @param query - A function that returns a Promise with the query result
|
|
771
|
-
* @param onMetadata - Callback function that receives execution metadata
|
|
849
|
+
* @param query - A function that returns a Promise with the query result. Can contain multiple database operations.
|
|
850
|
+
* @param onMetadata - Callback function that receives aggregated execution metadata
|
|
851
|
+
* @param onMetadata.totalDbExecutionTime - Total database execution time across all operations in the query function (in milliseconds)
|
|
852
|
+
* @param onMetadata.totalResponseSize - Total response size across all operations (in bytes)
|
|
853
|
+
* @param onMetadata.printQueries - Function to analyze and print query execution plans from CLUSTER_STATEMENTS_SUMMARY
|
|
772
854
|
* @returns Promise with the query result
|
|
855
|
+
*
|
|
773
856
|
* @example
|
|
774
857
|
* ```typescript
|
|
858
|
+
* // Basic usage with performance monitoring
|
|
775
859
|
* const result = await forgeSQL.executeWithMetadata(
|
|
776
|
-
* async () =>
|
|
777
|
-
*
|
|
778
|
-
*
|
|
779
|
-
*
|
|
780
|
-
*
|
|
860
|
+
* async () => {
|
|
861
|
+
* const users = await forgeSQL.selectFrom(usersTable);
|
|
862
|
+
* const orders = await forgeSQL.selectFrom(ordersTable).where(eq(ordersTable.userId, usersTable.id));
|
|
863
|
+
* return { users, orders };
|
|
864
|
+
* },
|
|
865
|
+
* (totalDbExecutionTime, totalResponseSize, printQueries) => {
|
|
866
|
+
* const threshold = 500; // ms baseline for this resolver
|
|
867
|
+
*
|
|
868
|
+
* if (totalDbExecutionTime > threshold * 1.5) {
|
|
869
|
+
* console.warn(`[Performance Warning] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
|
|
870
|
+
* await printQueries(); // Analyze and print query execution plans
|
|
871
|
+
* } else if (totalDbExecutionTime > threshold) {
|
|
872
|
+
* console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
|
|
873
|
+
* }
|
|
874
|
+
*
|
|
875
|
+
* console.log(`DB response size: ${totalResponseSize} bytes`);
|
|
781
876
|
* }
|
|
782
877
|
* );
|
|
783
878
|
* ```
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```typescript
|
|
882
|
+
* // Resolver with performance monitoring
|
|
883
|
+
* resolver.define("fetch", async (req: Request) => {
|
|
884
|
+
* try {
|
|
885
|
+
* return await forgeSQL.executeWithMetadata(
|
|
886
|
+
* async () => {
|
|
887
|
+
* // Resolver logic with multiple queries
|
|
888
|
+
* const users = await forgeSQL.selectFrom(demoUsers);
|
|
889
|
+
* const orders = await forgeSQL.selectFrom(demoOrders)
|
|
890
|
+
* .where(eq(demoOrders.userId, demoUsers.id));
|
|
891
|
+
* return { users, orders };
|
|
892
|
+
* },
|
|
893
|
+
* async (totalDbExecutionTime, totalResponseSize, printQueries) => {
|
|
894
|
+
* const threshold = 500; // ms baseline for this resolver
|
|
895
|
+
*
|
|
896
|
+
* if (totalDbExecutionTime > threshold * 1.5) {
|
|
897
|
+
* console.warn(`[Performance Warning fetch] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
|
|
898
|
+
* await printQueries(); // Optionally log or capture diagnostics for further analysis
|
|
899
|
+
* } else if (totalDbExecutionTime > threshold) {
|
|
900
|
+
* console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
|
|
901
|
+
* }
|
|
902
|
+
*
|
|
903
|
+
* console.log(`DB response size: ${totalResponseSize} bytes`);
|
|
904
|
+
* }
|
|
905
|
+
* );
|
|
906
|
+
* } catch (e) {
|
|
907
|
+
* const error = e?.cause?.debug?.sqlMessage ?? e?.cause;
|
|
908
|
+
* console.error(error, e);
|
|
909
|
+
* throw error;
|
|
910
|
+
* }
|
|
911
|
+
* });
|
|
912
|
+
* ```
|
|
913
|
+
*
|
|
914
|
+
* @note **Important**: When multiple resolvers are running concurrently, their query data may also appear in `printQueries()` analysis, as it queries the global `CLUSTER_STATEMENTS_SUMMARY` table.
|
|
784
915
|
*/
|
|
785
916
|
async executeWithMetadata<T>(
|
|
786
917
|
query: () => Promise<T>,
|
|
787
918
|
onMetadata: (
|
|
788
919
|
totalDbExecutionTime: number,
|
|
789
920
|
totalResponseSize: number,
|
|
790
|
-
|
|
921
|
+
printQueriesWithPlan: () => Promise<void>,
|
|
791
922
|
) => Promise<void> | void,
|
|
792
923
|
): Promise<T> {
|
|
793
924
|
return this.ormInstance.executeWithMetadata(query, onMetadata);
|