forge-sql-orm 2.1.16 → 2.1.18

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 CHANGED
@@ -23,7 +23,7 @@
23
23
  - ✅ **Custom Drizzle Driver** for direct integration with @forge/sql
24
24
  - ✅ **Local Cache System (Level 1)** for in-memory query optimization within single resolver invocation scope
25
25
  - ✅ **Global Cache System (Level 2)** with cross-invocation caching, automatic cache invalidation and context-aware operations (using [@forge/kvs](https://developer.atlassian.com/platform/forge/storage-reference/storage-api-custom-entities/) )
26
- - ✅ **Performance Monitoring**: Query execution metrics and analysis capabilities with automatic error analysis for timeout and OOM errors, plus scheduled slow query monitoring with execution plans
26
+ - ✅ **Performance Monitoring**: Query execution metrics and analysis capabilities with automatic error analysis for timeout and OOM errors, scheduled slow query monitoring with execution plans, and async query degradation analysis for non-blocking performance monitoring
27
27
  - ✅ **Type-Safe Query Building**: Write SQL queries with full TypeScript support
28
28
  - ✅ **Supports complex SQL queries** with joins and filtering using Drizzle ORM
29
29
  - ✅ **Advanced Query Methods**: `selectFrom()`, `selectDistinctFrom()`, `selectCacheableFrom()`, `selectDistinctCacheableFrom()` for all-column queries with field aliasing
@@ -2806,6 +2806,7 @@ All options are **optional**. If not specified, default values are used. You can
2806
2806
  | `topQueries` | `number` | `1` | Number of top slowest queries to analyze when `mode` is `'TopSlowest'` |
2807
2807
  | `showSlowestPlans` | `boolean` | `true` | Whether to show execution plans for slowest queries in TopSlowest mode. If `false`, only SQL and execution time are printed |
2808
2808
  | `normalizeQuery` | `boolean` | `true` | Whether to normalize SQL queries by replacing parameter values with `?` placeholders. Set to `false` to disable normalization if it causes issues with complex queries |
2809
+ | `asyncQueueName` | `string` | `""` | Queue name for async processing. If provided, query analysis will be queued for background processing instead of running synchronously. Requires consumer configuration in `manifest.yml` |
2809
2810
 
2810
2811
  **Examples:**
2811
2812
 
@@ -2891,6 +2892,194 @@ resolver.define("fetch", async (req: Request) => {
2891
2892
 
2892
2893
  > **💡 Tip**: When multiple resolvers are running concurrently, their query data may also appear in `printQueriesWithPlan()` analysis when using SummaryTable mode, as it queries the global `CLUSTER_STATEMENTS_SUMMARY` table.
2893
2894
 
2895
+ ### Async Query Degradation Analysis
2896
+
2897
+ Forge-SQL-ORM supports asynchronous processing of query degradation analysis, allowing you to offload performance analysis to a background queue. This is particularly useful for production environments where you want to avoid blocking resolver responses while still capturing detailed performance metrics.
2898
+
2899
+ #### Key Features
2900
+
2901
+ - **Non-Blocking Analysis**: Query analysis runs asynchronously without blocking resolver responses
2902
+ - **Automatic Fallback**: Falls back to synchronous execution if async queue fails
2903
+ - **Log Correlation**: Job IDs help correlate resolver logs with consumer logs
2904
+ - **Queue-Based Processing**: Uses Forge's event queue system for reliable processing
2905
+ - **Configurable Timeout**: Customizable timeout for event queuing (default: 1200ms)
2906
+
2907
+ #### Basic Setup
2908
+
2909
+ **1. Configure consumer in `manifest.yml`:**
2910
+
2911
+ ```yaml
2912
+ modules:
2913
+ consumer:
2914
+ - key: print-degradation-queries
2915
+ queue: degradationQueue
2916
+ function: handlerAsyncDegradation
2917
+
2918
+ function:
2919
+ - key: handlerAsyncDegradation
2920
+ handler: index.handlerAsyncDegradation
2921
+ ```
2922
+
2923
+ **2. Create the handler function:**
2924
+
2925
+ ```typescript
2926
+ import { AsyncEvent } from "@forge/events";
2927
+ import { printDegradationQueriesConsumer } from "forge-sql-orm";
2928
+ import { FORGE_SQL_ORM } from "./utils/forgeSqlOrmUtils";
2929
+
2930
+ export const handlerAsyncDegradation = (event: AsyncEvent) => {
2931
+ return printDegradationQueriesConsumer(FORGE_SQL_ORM, event);
2932
+ };
2933
+ ```
2934
+
2935
+ **3. Enable async processing in resolver:**
2936
+
2937
+ ```typescript
2938
+ resolver.define("fetch", async (req: Request) => {
2939
+ return await FORGE_SQL_ORM.executeWithMetadata(
2940
+ async () => {
2941
+ // ... your queries ...
2942
+ return await SQL_QUERY;
2943
+ },
2944
+ async (totalDbExecutionTime, totalResponseSize, printQueries) => {
2945
+ if (totalDbExecutionTime > 800) {
2946
+ await printQueries(); // Will queue for async processing
2947
+ }
2948
+ },
2949
+ { asyncQueueName: "degradationQueue" }, // Enable async processing
2950
+ );
2951
+ });
2952
+ ```
2953
+
2954
+ #### Configuration Options
2955
+
2956
+ The `asyncQueueName` option enables async processing:
2957
+
2958
+ | Option | Type | Default | Description |
2959
+ | ---------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
2960
+ | `asyncQueueName` | `string` | `""` | Queue name for async processing. If provided, query analysis will be queued instead of running synchronously. If empty or not provided, runs synchronously |
2961
+
2962
+ #### How It Works
2963
+
2964
+ 1. **Resolver Execution**: When `printQueriesWithPlan()` is called with `asyncQueueName` configured:
2965
+ - Creates an event payload with query statistics and metadata
2966
+ - Sends event to the specified queue with a timeout (default: 1200ms)
2967
+ - Logs a warning message with Job ID for correlation
2968
+ - Returns immediately without waiting for analysis
2969
+
2970
+ 2. **Async Processing**: The consumer function (`handlerAsyncDegradation`):
2971
+ - Receives the event from the queue
2972
+ - Logs processing start with Job ID
2973
+ - Executes query analysis (TopSlowest or SummaryTable mode)
2974
+ - Prints execution plans and performance metrics
2975
+
2976
+ 3. **Fallback Behavior**: If queue push fails or times out:
2977
+ - Falls back to synchronous execution automatically
2978
+ - Logs a warning message
2979
+ - Analysis still completes, just synchronously
2980
+
2981
+ #### Log Correlation
2982
+
2983
+ Both resolver and consumer logs include Job IDs to help you correlate related events:
2984
+
2985
+ **Resolver log (when event is queued):**
2986
+
2987
+ ```
2988
+ WARN [Performance Analysis] Query degradation event queued for async processing | Job ID: abc-123 | Total DB time: 3531ms | Queries: 3 | Look for consumer log with jobId: abc-123
2989
+ ```
2990
+
2991
+ **Consumer log (when event is processed):**
2992
+
2993
+ ```
2994
+ WARN [Performance Analysis] Processing query degradation event | Job ID: abc-123 | Total DB time: 3531ms | Queries: 3 | Started: 2025-12-15T18:12:34.251Z
2995
+ WARN SQL: SELECT ... | Time: 3514 ms
2996
+ Plan:
2997
+ Projection_7 | task:root | ...
2998
+ ```
2999
+
3000
+ **To find all related logs:**
3001
+
3002
+ - Search logs for: `"Job ID: abc-123"`
3003
+ - This will show both the queuing event and the processing event
3004
+
3005
+ #### Example: Complete Setup
3006
+
3007
+ **manifest.yml:**
3008
+
3009
+ ```yaml
3010
+ modules:
3011
+ consumer:
3012
+ - key: print-degradation-queries
3013
+ queue: degradationQueue
3014
+ function: handlerAsyncDegradation
3015
+
3016
+ function:
3017
+ - key: handlerAsyncDegradation
3018
+ handler: index.handlerAsyncDegradation
3019
+ ```
3020
+
3021
+ **index.ts:**
3022
+
3023
+ ```typescript
3024
+ import { AsyncEvent } from "@forge/events";
3025
+ import { printDegradationQueriesConsumer } from "forge-sql-orm";
3026
+ import { FORGE_SQL_ORM } from "./utils/forgeSqlOrmUtils";
3027
+
3028
+ // Consumer handler
3029
+ export const handlerAsyncDegradation = (event: AsyncEvent) => {
3030
+ return printDegradationQueriesConsumer(FORGE_SQL_ORM, event);
3031
+ };
3032
+
3033
+ // Resolver with async analysis
3034
+ resolver.define("fetch", async (req: Request) => {
3035
+ return await FORGE_SQL_ORM.executeWithMetadata(
3036
+ async () => {
3037
+ const users = await FORGE_SQL_ORM.selectFrom(demoUsers);
3038
+ const orders = await FORGE_SQL_ORM.selectFrom(demoOrders);
3039
+ return { users, orders };
3040
+ },
3041
+ async (totalDbExecutionTime, totalResponseSize, printQueries) => {
3042
+ const threshold = 800; // ms baseline
3043
+
3044
+ if (totalDbExecutionTime > threshold) {
3045
+ console.warn(`[Performance Warning] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
3046
+ await printQueries(); // Queued for async processing
3047
+ }
3048
+ },
3049
+ {
3050
+ asyncQueueName: "degradationQueue", // Enable async processing
3051
+ mode: "TopSlowest",
3052
+ topQueries: 1,
3053
+ },
3054
+ );
3055
+ });
3056
+ ```
3057
+
3058
+ #### Benefits
3059
+
3060
+ - **Non-Blocking**: Resolver responses are not delayed by query analysis
3061
+ - **Production Ready**: Suitable for production environments where performance is critical
3062
+ - **Reliable**: Automatic fallback ensures analysis always completes
3063
+ - **Traceable**: Job IDs enable easy log correlation
3064
+ - **Scalable**: Queue-based processing handles high load scenarios
3065
+
3066
+ #### When to Use Async Processing
3067
+
3068
+ **Use async processing when:**
3069
+
3070
+ - You're in a production environment
3071
+ - Resolver response time is critical
3072
+ - You want to avoid blocking user requests
3073
+ - You need detailed analysis but can process it later
3074
+
3075
+ **Use synchronous processing when:**
3076
+
3077
+ - You're in development/debugging
3078
+ - You need immediate analysis results
3079
+ - You want simpler setup (no queue configuration)
3080
+
3081
+ > **💡 Tip**: The async queue name must match the queue name configured in your `manifest.yml` consumer section. If the queue doesn't exist or the event fails to send, the system automatically falls back to synchronous execution.
3082
+
2894
3083
  ### Slow Query Monitoring
2895
3084
 
2896
3085
  Forge-SQL-ORM provides a scheduler trigger (`slowQuerySchedulerTrigger`) that automatically monitors and analyzes slow queries on an hourly basis. This trigger queries TiDB's slow query log system table and provides detailed performance information including SQL query text, memory usage, execution time, and execution plans.
@@ -0,0 +1,98 @@
1
+ import { AsyncEvent } from "@forge/events";
2
+ import { MetadataQueryOptions, Statistic } from "../utils/metadataContextUtils";
3
+ import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
4
+ /**
5
+ * Event payload for async query degradation analysis.
6
+ * Contains query performance statistics and metadata for analysis.
7
+ */
8
+ export type AsyncEventPrintQuery = {
9
+ /** Total database execution time across all queries in milliseconds */
10
+ totalDbExecutionTime: number;
11
+ /** Total response size across all queries in bytes */
12
+ totalResponseSize: number;
13
+ /** Timestamp when the query execution started */
14
+ beginTime: Date;
15
+ /** Query analysis options with all fields set to defaults if not provided */
16
+ options: Required<MetadataQueryOptions>;
17
+ /** Array of query statistics including SQL, parameters, and execution metadata */
18
+ statistics: Statistic[];
19
+ };
20
+ /**
21
+ * Consumer function for processing async query degradation events.
22
+ *
23
+ * This function is called by the Forge event system when a query degradation event
24
+ * is received from the queue. It processes the event and prints query performance
25
+ * analysis including execution plans for slow queries.
26
+ *
27
+ * The function logs a warning message with job ID, total DB time, query count, and
28
+ * start time to help correlate with the original resolver/service logs.
29
+ *
30
+ * @param forgeSQLORM - The ForgeSQL operation instance for database access
31
+ * @param event - The async event containing query degradation data
32
+ * @returns Promise that resolves when query analysis is complete
33
+ *
34
+ * @example
35
+ * ```yaml
36
+ * # manifest.yml - Configure consumer for async query degradation analysis
37
+ * modules:
38
+ * consumer:
39
+ * - key: print-degradation-queries
40
+ * queue: degradationQueue
41
+ * function: handlerAsyncDegradation
42
+ * function:
43
+ * - key: handlerAsyncDegradation
44
+ * handler: index.handlerAsyncDegradation
45
+ * ```
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * // index.ts - Handler function that processes async events
50
+ * import { AsyncEvent } from "@forge/events";
51
+ * import { printDegradationQueriesConsumer } from "forge-sql-orm";
52
+ * import { FORGE_SQL_ORM } from "./utils/forgeSqlOrmUtils";
53
+ *
54
+ * export const handlerAsyncDegradation = (event: AsyncEvent) => {
55
+ * return printDegradationQueriesConsumer(FORGE_SQL_ORM, event);
56
+ * };
57
+ * ```
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * // Using async queue in resolver - Enable async processing
62
+ * resolver.define("fetch", async (req: Request) => {
63
+ * return await FORGE_SQL_ORM.executeWithMetadata(
64
+ * async () => {
65
+ * // ... your queries ...
66
+ * return await SQL_QUERY;
67
+ * },
68
+ * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
69
+ * if (totalDbExecutionTime > 800) {
70
+ * await printQueries(); // Will queue for async processing
71
+ * }
72
+ * },
73
+ * { asyncQueueName: "degradationQueue" } // Enable async processing
74
+ * );
75
+ * });
76
+ * ```
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * // Log correlation - How to find related logs
81
+ * //
82
+ * // 1. In resolver/service log, you'll see:
83
+ * // [Performance Analysis] Query degradation event queued for async processing | Job ID: abc-123 | ...
84
+ * //
85
+ * // 2. In consumer log (handlerAsyncDegradation), search for the same Job ID:
86
+ * // [Performance Analysis] Processing query degradation event | Job ID: abc-123 | ...
87
+ * //
88
+ * // 3. To find all related logs, search logs for: "Job ID: abc-123"
89
+ * // This will show both the queuing event and the processing event
90
+ * //
91
+ * // Example log flow:
92
+ * // WARN resolver: [Performance Analysis] Query degradation event queued... | Job ID: abc-123
93
+ * // WARN handlerAsyncDegradation: [Performance Analysis] Processing query degradation event | Job ID: abc-123
94
+ * // WARN handlerAsyncDegradation: SQL: SELECT ... | Time: 3514 ms
95
+ * ```
96
+ */
97
+ export declare function printDegradationQueriesConsumer(forgeSQLORM: ForgeSqlOperation, event: AsyncEvent): Promise<void>;
98
+ //# sourceMappingURL=PrintQueryConsumer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrintQueryConsumer.d.ts","sourceRoot":"","sources":["../../src/async/PrintQueryConsumer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EACL,oBAAoB,EAEpB,SAAS,EACV,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,uEAAuE;IACvE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sDAAsD;IACtD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iDAAiD;IACjD,SAAS,EAAE,IAAI,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IACxC,kFAAkF;IAClF,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4EG;AACH,wBAAsB,+BAA+B,CACnD,WAAW,EAAE,iBAAiB,EAC9B,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printDegradationQueriesConsumer = printDegradationQueriesConsumer;
4
+ const metadataContextUtils_1 = require("../utils/metadataContextUtils");
5
+ /**
6
+ * Consumer function for processing async query degradation events.
7
+ *
8
+ * This function is called by the Forge event system when a query degradation event
9
+ * is received from the queue. It processes the event and prints query performance
10
+ * analysis including execution plans for slow queries.
11
+ *
12
+ * The function logs a warning message with job ID, total DB time, query count, and
13
+ * start time to help correlate with the original resolver/service logs.
14
+ *
15
+ * @param forgeSQLORM - The ForgeSQL operation instance for database access
16
+ * @param event - The async event containing query degradation data
17
+ * @returns Promise that resolves when query analysis is complete
18
+ *
19
+ * @example
20
+ * ```yaml
21
+ * # manifest.yml - Configure consumer for async query degradation analysis
22
+ * modules:
23
+ * consumer:
24
+ * - key: print-degradation-queries
25
+ * queue: degradationQueue
26
+ * function: handlerAsyncDegradation
27
+ * function:
28
+ * - key: handlerAsyncDegradation
29
+ * handler: index.handlerAsyncDegradation
30
+ * ```
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * // index.ts - Handler function that processes async events
35
+ * import { AsyncEvent } from "@forge/events";
36
+ * import { printDegradationQueriesConsumer } from "forge-sql-orm";
37
+ * import { FORGE_SQL_ORM } from "./utils/forgeSqlOrmUtils";
38
+ *
39
+ * export const handlerAsyncDegradation = (event: AsyncEvent) => {
40
+ * return printDegradationQueriesConsumer(FORGE_SQL_ORM, event);
41
+ * };
42
+ * ```
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * // Using async queue in resolver - Enable async processing
47
+ * resolver.define("fetch", async (req: Request) => {
48
+ * return await FORGE_SQL_ORM.executeWithMetadata(
49
+ * async () => {
50
+ * // ... your queries ...
51
+ * return await SQL_QUERY;
52
+ * },
53
+ * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
54
+ * if (totalDbExecutionTime > 800) {
55
+ * await printQueries(); // Will queue for async processing
56
+ * }
57
+ * },
58
+ * { asyncQueueName: "degradationQueue" } // Enable async processing
59
+ * );
60
+ * });
61
+ * ```
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * // Log correlation - How to find related logs
66
+ * //
67
+ * // 1. In resolver/service log, you'll see:
68
+ * // [Performance Analysis] Query degradation event queued for async processing | Job ID: abc-123 | ...
69
+ * //
70
+ * // 2. In consumer log (handlerAsyncDegradation), search for the same Job ID:
71
+ * // [Performance Analysis] Processing query degradation event | Job ID: abc-123 | ...
72
+ * //
73
+ * // 3. To find all related logs, search logs for: "Job ID: abc-123"
74
+ * // This will show both the queuing event and the processing event
75
+ * //
76
+ * // Example log flow:
77
+ * // WARN resolver: [Performance Analysis] Query degradation event queued... | Job ID: abc-123
78
+ * // WARN handlerAsyncDegradation: [Performance Analysis] Processing query degradation event | Job ID: abc-123
79
+ * // WARN handlerAsyncDegradation: SQL: SELECT ... | Time: 3514 ms
80
+ * ```
81
+ */
82
+ async function printDegradationQueriesConsumer(forgeSQLORM, event) {
83
+ const body = event.body;
84
+ body.beginTime = new Date(body.beginTime);
85
+ // eslint-disable-next-line no-console
86
+ console.warn(`[Performance Analysis] Processing query degradation event | Job ID: ${event.jobId} | Total DB time: ${body.totalDbExecutionTime}ms | Queries: ${body.statistics.length} | Started: ${body.beginTime.toISOString()}`);
87
+ await (0, metadataContextUtils_1.printDegradationQueries)(forgeSQLORM, body);
88
+ }
89
+ //# sourceMappingURL=PrintQueryConsumer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrintQueryConsumer.js","sourceRoot":"","sources":["../../src/async/PrintQueryConsumer.ts"],"names":[],"mappings":";;AAsGA,0EAWC;AAhHD,wEAIuC;AAoBvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4EG;AACI,KAAK,UAAU,+BAA+B,CACnD,WAA8B,EAC9B,KAAiB;IAEjB,MAAM,IAAI,GAAyB,KAAK,CAAC,IAA4B,CAAC;IACtE,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1C,sCAAsC;IACtC,OAAO,CAAC,IAAI,CACV,uEAAuE,KAAK,CAAC,KAAK,qBAAqB,IAAI,CAAC,oBAAoB,iBAAiB,IAAI,CAAC,UAAU,CAAC,MAAM,eAAe,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,CACrN,CAAC;IACF,MAAM,IAAA,8CAAuB,EAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AACnD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -7,4 +7,5 @@ export * from "./utils/forgeDriver";
7
7
  export * from "./webtriggers";
8
8
  export * from "./lib/drizzle/extensions/additionalActions";
9
9
  export * from "./core/SystemTables";
10
+ export * from "./async/PrintQueryConsumer";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iCAAiC,CAAC;AAChD,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,4CAA4C,CAAC;AAC3D,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iCAAiC,CAAC;AAChD,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,4CAA4C,CAAC;AAC3D,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC"}
package/dist/index.js CHANGED
@@ -28,4 +28,5 @@ __exportStar(require("./utils/forgeDriver"), exports);
28
28
  __exportStar(require("./webtriggers"), exports);
29
29
  __exportStar(require("./lib/drizzle/extensions/additionalActions"), exports);
30
30
  __exportStar(require("./core/SystemTables"), exports);
31
+ __exportStar(require("./async/PrintQueryConsumer"), exports);
31
32
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,kDAA6C;AAApC,uHAAA,OAAO,OAAA;AAEhB,8DAA4C;AAC5C,gEAA8C;AAC9C,kEAAgD;AAChD,mDAAiC;AACjC,sDAAoC;AACpC,gDAA8B;AAC9B,6EAA2D;AAC3D,sDAAoC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,kDAA6C;AAApC,uHAAA,OAAO,OAAA;AAEhB,8DAA4C;AAC5C,gEAA8C;AAC9C,kEAAgD;AAChD,mDAAiC;AACjC,sDAAoC;AACpC,gDAA8B;AAC9B,6EAA2D;AAC3D,sDAAoC;AACpC,6DAA2C"}
@@ -40,6 +40,11 @@ function createForgeDriverProxy(forgeSqlOperation, options, logRawSqlQuery) {
40
40
  const isTimeoutError = error.code === QUERY_ERROR_CODES.TIMEOUT;
41
41
  const isOutOfMemoryError = error?.context?.debug?.errno === QUERY_ERROR_CODES.OUT_OF_MEMORY_ERRNO;
42
42
  if (isTimeoutError || isOutOfMemoryError) {
43
+ // Wait for CLUSTER_STATEMENTS_SUMMARY to be populated with our failed query data
44
+ await new Promise((resolve) => setTimeout(resolve, STATEMENTS_SUMMARY_DELAY_MS));
45
+ const queryEndTime = Date.now();
46
+ const queryDuration = queryEndTime - queryStartTime;
47
+ let errorType = "TIMEOUT";
43
48
  if (isTimeoutError) {
44
49
  // eslint-disable-next-line no-console
45
50
  console.error(` TIMEOUT detected - Query exceeded time limit`);
@@ -47,13 +52,10 @@ function createForgeDriverProxy(forgeSqlOperation, options, logRawSqlQuery) {
47
52
  else {
48
53
  // eslint-disable-next-line no-console
49
54
  console.error(`OUT OF MEMORY detected - Query exceeded memory limit`);
55
+ errorType = "OOM";
50
56
  }
51
- // Wait for CLUSTER_STATEMENTS_SUMMARY to be populated with our failed query data
52
- await new Promise((resolve) => setTimeout(resolve, STATEMENTS_SUMMARY_DELAY_MS));
53
- const queryEndTime = Date.now();
54
- const queryDuration = queryEndTime - queryStartTime;
55
57
  // Analyze the failed query using CLUSTER_STATEMENTS_SUMMARY
56
- await (0, sqlUtils_1.printQueriesWithPlan)(forgeSqlOperation, queryDuration);
58
+ await (0, sqlUtils_1.handleErrorsWithPlan)(forgeSqlOperation, queryDuration, errorType);
57
59
  }
58
60
  // Log SQL error details if requested
59
61
  if (logRawSqlQuery) {
@@ -1 +1 @@
1
- {"version":3,"file":"forgeDriverProxy.js","sourceRoot":"","sources":["../../src/utils/forgeDriverProxy.ts"],"names":[],"mappings":";;AAyBA,wDA8DC;AAvFD,+CAA4C;AAC5C,yCAAsD;AAEtD,yCAAkD;AAElD;;GAEG;AACH,MAAM,iBAAiB,GAAG;IACxB,OAAO,EAAE,mBAAmB;IAC5B,mBAAmB,EAAE,IAAI;CACjB,CAAC;AAEX;;GAEG;AACH,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAExC;;;;;;GAMG;AACH,SAAgB,sBAAsB,CACpC,iBAAoC,EACpC,OAAkB,EAClB,cAAwB;IAExB,OAAO,KAAK,EACV,KAAa,EACb,MAAa,EACb,MAAyB,EAKxB,EAAE;QACH,kCAAkC;QAClC,MAAM,aAAa,GAAG,IAAA,yBAAc,EAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAErD,IAAI,OAAO,IAAI,cAAc,IAAI,aAAa,KAAK,KAAK,EAAE,CAAC;YACzD,sCAAsC;YACtC,OAAO,CAAC,KAAK,CAAC,uBAAuB,aAAa,EAAE,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAElC,IAAI,CAAC;YACH,wCAAwC;YACxC,OAAO,MAAM,IAAA,yBAAW,EAAC,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,4EAA4E;YAC5E,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,OAAO,CAAC;YAChE,MAAM,kBAAkB,GACtB,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,iBAAiB,CAAC,mBAAmB,CAAC;YAEzE,IAAI,cAAc,IAAI,kBAAkB,EAAE,CAAC;gBACzC,IAAI,cAAc,EAAE,CAAC;oBACnB,sCAAsC;oBACtC,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;gBACjE,CAAC;qBAAM,CAAC;oBACN,sCAAsC;oBACtC,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;gBACxE,CAAC;gBAED,iFAAiF;gBACjF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC;gBAEjF,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAChC,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,CAAC;gBAEpD,4DAA4D;gBAC5D,MAAM,IAAA,+BAAoB,EAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;YAC/D,CAAC;YAED,qCAAqC;YACrC,IAAI,cAAc,EAAE,CAAC;gBACnB,sCAAsC;gBACtC,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACtE,CAAC;YAED,8BAA8B;YAC9B,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"forgeDriverProxy.js","sourceRoot":"","sources":["../../src/utils/forgeDriverProxy.ts"],"names":[],"mappings":";;AAyBA,wDA8DC;AAvFD,+CAA4C;AAC5C,yCAAsD;AAEtD,yCAAkD;AAElD;;GAEG;AACH,MAAM,iBAAiB,GAAG;IACxB,OAAO,EAAE,mBAAmB;IAC5B,mBAAmB,EAAE,IAAI;CACjB,CAAC;AAEX;;GAEG;AACH,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAExC;;;;;;GAMG;AACH,SAAgB,sBAAsB,CACpC,iBAAoC,EACpC,OAAkB,EAClB,cAAwB;IAExB,OAAO,KAAK,EACV,KAAa,EACb,MAAa,EACb,MAAyB,EAKxB,EAAE;QACH,kCAAkC;QAClC,MAAM,aAAa,GAAG,IAAA,yBAAc,EAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAErD,IAAI,OAAO,IAAI,cAAc,IAAI,aAAa,KAAK,KAAK,EAAE,CAAC;YACzD,sCAAsC;YACtC,OAAO,CAAC,KAAK,CAAC,uBAAuB,aAAa,EAAE,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAElC,IAAI,CAAC;YACH,wCAAwC;YACxC,OAAO,MAAM,IAAA,yBAAW,EAAC,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,4EAA4E;YAC5E,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,OAAO,CAAC;YAChE,MAAM,kBAAkB,GACtB,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,iBAAiB,CAAC,mBAAmB,CAAC;YAEzE,IAAI,cAAc,IAAI,kBAAkB,EAAE,CAAC;gBACzC,iFAAiF;gBACjF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC;gBAEjF,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAChC,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,CAAC;gBACpD,IAAI,SAAS,GAAsB,SAAS,CAAC;gBAC7C,IAAI,cAAc,EAAE,CAAC;oBACnB,sCAAsC;oBACtC,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;gBACjE,CAAC;qBAAM,CAAC;oBACN,sCAAsC;oBACtC,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;oBACtE,SAAS,GAAG,KAAK,CAAC;gBACpB,CAAC;gBACD,4DAA4D;gBAC5D,MAAM,IAAA,+BAAoB,EAAC,iBAAiB,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;YAC1E,CAAC;YAED,qCAAqC;YACrC,IAAI,cAAc,EAAE,CAAC;gBACnB,sCAAsC;gBACtC,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACtE,CAAC;YAED,8BAA8B;YAC9B,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -1,7 +1,8 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { ForgeSQLMetadata } from "./forgeDriver";
3
3
  import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
4
- type Statistic = {
4
+ import { AsyncEventPrintQuery } from "../async/PrintQueryConsumer";
5
+ export type Statistic = {
5
6
  query: string;
6
7
  params: unknown[];
7
8
  metadata: ForgeSQLMetadata;
@@ -13,6 +14,7 @@ export type MetadataQueryOptions = {
13
14
  topQueries?: number;
14
15
  showSlowestPlans?: boolean;
15
16
  normalizeQuery?: boolean;
17
+ asyncQueueName?: string;
16
18
  };
17
19
  export type MetadataQueryContext = {
18
20
  totalDbExecutionTime: number;
@@ -26,15 +28,53 @@ export type MetadataQueryContext = {
26
28
  export declare const metadataQueryContext: AsyncLocalStorage<MetadataQueryContext>;
27
29
  /**
28
30
  * Saves query metadata to the current context and sets up the printQueriesWithPlan function.
31
+ *
32
+ * This function accumulates query statistics in the async context. When printQueriesWithPlan
33
+ * is called, it can either:
34
+ * - Queue the analysis for async processing (if asyncQueueName is provided)
35
+ * - Execute the analysis synchronously (fallback or if asyncQueueName is not set)
36
+ *
37
+ * For async processing, the function sends an event to the specified queue with a timeout.
38
+ * If the event cannot be sent within the timeout, it falls back to synchronous execution.
39
+ *
29
40
  * @param stringQuery - The SQL query string
30
- * @param params - Query parameters
31
- * @param metadata - Query execution metadata
41
+ * @param params - Query parameters used in the query
42
+ * @param metadata - Query execution metadata including execution time and response size
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * await FORGE_SQL_ORM.executeWithMetadata(
47
+ * async () => {
48
+ * // ... queries ...
49
+ * },
50
+ * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
51
+ * if (totalDbExecutionTime > threshold) {
52
+ * await printQueries(); // Will use async queue if configured
53
+ * }
54
+ * },
55
+ * { asyncQueueName: "degradationQueue" }
56
+ * );
57
+ * ```
32
58
  */
33
59
  export declare function saveMetaDataToContext(stringQuery: string, params: unknown[], metadata: ForgeSQLMetadata): Promise<void>;
60
+ /**
61
+ * Prints query degradation analysis for the provided event payload.
62
+ *
63
+ * This function processes query degradation events (either from async queue or synchronous call).
64
+ * It first attempts to use summary tables (CLUSTER_STATEMENTS_SUMMARY) if configured and within
65
+ * the time window. Otherwise, it falls back to printing execution plans for the top slowest queries.
66
+ *
67
+ * @param forgeSQLORM - The ForgeSQL operation instance for database access
68
+ * @param params - The async event payload containing query statistics, options, and metadata
69
+ * @returns Promise that resolves when query analysis is complete
70
+ *
71
+ * @see printPlansUsingSummaryTables - For summary table analysis
72
+ * @see printTopQueriesPlans - For top slowest queries analysis
73
+ */
74
+ export declare function printDegradationQueries(forgeSQLORM: ForgeSqlOperation, params: AsyncEventPrintQuery): Promise<void>;
34
75
  /**
35
76
  * Gets the latest metadata from the current context.
36
77
  * @returns The current metadata context or undefined if not in a context
37
78
  */
38
79
  export declare function getLastestMetadata(): Promise<MetadataQueryContext | undefined>;
39
- export {};
40
80
  //# 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;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAOjE,KAAK,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAC;IAAC,QAAQ,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAElF,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,CAAC;AAE1D,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAEF,eAAO,MAAM,oBAAoB,yCAAgD,CAAC;AAkOlF;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,OAAO,EAAE,EACjB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,IAAI,CAAC,CAoCf;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAEpF"}
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;AAKjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAKnE,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAC;IAAC,QAAQ,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAEzF,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,CAAC;AAE1D,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAEF,eAAO,MAAM,oBAAoB,yCAAgD,CAAC;AA8OlF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,OAAO,EAAE,EACjB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,IAAI,CAAC,CA8Df;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,iBAAiB,EAC9B,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAEpF"}