forge-sql-orm 2.1.3 → 2.1.5

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.
Files changed (52) hide show
  1. package/README.md +291 -0
  2. package/dist/ForgeSQLORM.js +713 -12
  3. package/dist/ForgeSQLORM.js.map +1 -1
  4. package/dist/ForgeSQLORM.mjs +716 -15
  5. package/dist/ForgeSQLORM.mjs.map +1 -1
  6. package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
  7. package/dist/core/ForgeSQLORM.d.ts +23 -0
  8. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  9. package/dist/core/ForgeSQLQueryBuilder.d.ts +36 -5
  10. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  11. package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
  12. package/dist/core/SystemTables.d.ts +5039 -0
  13. package/dist/core/SystemTables.d.ts.map +1 -1
  14. package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -1
  15. package/dist/utils/cacheContextUtils.d.ts.map +1 -1
  16. package/dist/utils/cacheUtils.d.ts.map +1 -1
  17. package/dist/utils/forgeDriver.d.ts +21 -0
  18. package/dist/utils/forgeDriver.d.ts.map +1 -1
  19. package/dist/utils/forgeDriverProxy.d.ts.map +1 -1
  20. package/dist/utils/metadataContextUtils.d.ts +11 -0
  21. package/dist/utils/metadataContextUtils.d.ts.map +1 -0
  22. package/dist/utils/sqlUtils.d.ts.map +1 -1
  23. package/dist/webtriggers/applyMigrationsWebTrigger.d.ts +1 -1
  24. package/dist/webtriggers/applyMigrationsWebTrigger.d.ts.map +1 -1
  25. package/dist/webtriggers/clearCacheSchedulerTrigger.d.ts.map +1 -1
  26. package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
  27. package/dist/webtriggers/dropTablesMigrationWebTrigger.d.ts.map +1 -1
  28. package/dist/webtriggers/fetchSchemaWebTrigger.d.ts.map +1 -1
  29. package/dist/webtriggers/index.d.ts +1 -0
  30. package/dist/webtriggers/index.d.ts.map +1 -1
  31. package/dist/webtriggers/topSlowestStatementLastHourTrigger.d.ts +89 -0
  32. package/dist/webtriggers/topSlowestStatementLastHourTrigger.d.ts.map +1 -0
  33. package/package.json +4 -3
  34. package/src/core/ForgeSQLCrudOperations.ts +3 -0
  35. package/src/core/ForgeSQLORM.ts +119 -51
  36. package/src/core/ForgeSQLQueryBuilder.ts +51 -17
  37. package/src/core/ForgeSQLSelectOperations.ts +2 -0
  38. package/src/core/SystemTables.ts +313 -1
  39. package/src/lib/drizzle/extensions/additionalActions.ts +2 -0
  40. package/src/utils/cacheContextUtils.ts +4 -2
  41. package/src/utils/cacheUtils.ts +20 -8
  42. package/src/utils/forgeDriver.ts +22 -1
  43. package/src/utils/forgeDriverProxy.ts +2 -0
  44. package/src/utils/metadataContextUtils.ts +24 -0
  45. package/src/utils/sqlUtils.ts +1 -0
  46. package/src/webtriggers/applyMigrationsWebTrigger.ts +10 -5
  47. package/src/webtriggers/clearCacheSchedulerTrigger.ts +1 -0
  48. package/src/webtriggers/dropMigrationWebTrigger.ts +2 -0
  49. package/src/webtriggers/dropTablesMigrationWebTrigger.ts +2 -0
  50. package/src/webtriggers/fetchSchemaWebTrigger.ts +1 -0
  51. package/src/webtriggers/index.ts +1 -0
  52. package/src/webtriggers/topSlowestStatementLastHourTrigger.ts +360 -0
@@ -1,4 +1,4 @@
1
- import { isTable, sql, eq, and, getTableColumns } from "drizzle-orm";
1
+ import { isTable, sql, eq, and, getTableColumns, gte, desc } from "drizzle-orm";
2
2
  import { DateTime } from "luxon";
3
3
  import { isSQLWrapper } from "drizzle-orm/sql/sql";
4
4
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -7,7 +7,7 @@ import * as crypto from "crypto";
7
7
  import { kvs, WhereConditions, Filter, FilterConditions } from "@forge/kvs";
8
8
  import { sql as sql$1, migrationRunner } from "@forge/sql";
9
9
  import { drizzle } from "drizzle-orm/mysql-proxy";
10
- import { customType, mysqlTable, timestamp, varchar, bigint } from "drizzle-orm/mysql-core";
10
+ import { customType, mysqlTable, timestamp, varchar, bigint, mysqlSchema, longtext, double, boolean, text, int, unionAll } from "drizzle-orm/mysql-core";
11
11
  const parseDateTime = (value, format) => {
12
12
  let result;
13
13
  if (value instanceof Date) {
@@ -391,7 +391,7 @@ async function clearCursorCache(tables, cursor, options) {
391
391
  entityQueryBuilder = entityQueryBuilder.cursor(cursor);
392
392
  }
393
393
  const listResult = await entityQueryBuilder.limit(100).getMany();
394
- if (options.logRawSqlQuery) {
394
+ if (options.logCache) {
395
395
  console.warn(`clear cache Records: ${JSON.stringify(listResult.results.map((r) => r.key))}`);
396
396
  }
397
397
  await deleteCacheEntriesInBatches(listResult.results, cacheEntityName);
@@ -412,7 +412,7 @@ async function clearExpirationCursorCache(cursor, options) {
412
412
  entityQueryBuilder = entityQueryBuilder.cursor(cursor);
413
413
  }
414
414
  const listResult = await entityQueryBuilder.limit(100).getMany();
415
- if (options.logRawSqlQuery) {
415
+ if (options.logCache) {
416
416
  console.warn(`clear expired Records: ${JSON.stringify(listResult.results.map((r) => r.key))}`);
417
417
  }
418
418
  await deleteCacheEntriesInBatches(listResult.results, cacheEntityName);
@@ -461,7 +461,7 @@ async function clearTablesCache(tables, options) {
461
461
  "clearing cache"
462
462
  );
463
463
  } finally {
464
- if (options.logRawSqlQuery) {
464
+ if (options.logCache) {
465
465
  const duration = DateTime.now().toSeconds() - startTime.toSeconds();
466
466
  console.info(`Cleared ${totalRecords} cache records in ${duration} seconds`);
467
467
  }
@@ -480,7 +480,7 @@ async function clearExpiredCache(options) {
480
480
  );
481
481
  } finally {
482
482
  const duration = DateTime.now().toSeconds() - startTime.toSeconds();
483
- if (options?.logRawSqlQuery) {
483
+ if (options?.logCache) {
484
484
  console.debug(`Cleared ${totalRecords} expired cache records in ${duration} seconds`);
485
485
  }
486
486
  }
@@ -495,7 +495,7 @@ async function getFromCache(query, options) {
495
495
  const sqlQuery = query.toSQL();
496
496
  const key = hashKey(sqlQuery);
497
497
  if (await isTableContainsTableInCacheContext(sqlQuery.sql, options)) {
498
- if (options.logRawSqlQuery) {
498
+ if (options.logCache) {
499
499
  console.warn(`Context contains value to clear. Skip getting from cache`);
500
500
  }
501
501
  return void 0;
@@ -503,7 +503,7 @@ async function getFromCache(query, options) {
503
503
  try {
504
504
  const cacheResult = await kvs.entity(options.cacheEntityName).get(key);
505
505
  if (cacheResult && cacheResult[expirationName] >= getCurrentTime() && sqlQuery.sql.toLowerCase() === cacheResult[entityQueryName]) {
506
- if (options.logRawSqlQuery) {
506
+ if (options.logCache) {
507
507
  console.warn(`Get value from cache, cacheKey: ${key}`);
508
508
  }
509
509
  const results = cacheResult[dataName];
@@ -524,7 +524,7 @@ async function setCacheResult(query, options, results, cacheTtl) {
524
524
  const dataName = options.cacheEntityDataName ?? CACHE_CONSTANTS.DEFAULT_DATA_NAME;
525
525
  const sqlQuery = query.toSQL();
526
526
  if (await isTableContainsTableInCacheContext(sqlQuery.sql, options)) {
527
- if (options.logRawSqlQuery) {
527
+ if (options.logCache) {
528
528
  console.warn(`Context contains value to clear. Skip setting from cache`);
529
529
  }
530
530
  return;
@@ -539,7 +539,7 @@ async function setCacheResult(query, options, results, cacheTtl) {
539
539
  },
540
540
  { entityName: options.cacheEntityName }
541
541
  ).execute();
542
- if (options.logRawSqlQuery) {
542
+ if (options.logCache) {
543
543
  console.warn(`Store value to cache, cacheKey: ${key}`);
544
544
  }
545
545
  } catch (error) {
@@ -567,7 +567,7 @@ async function saveQueryLocalCacheQuery(query, rows, options) {
567
567
  sql: sql2.toSQL().sql.toLowerCase(),
568
568
  data: rows
569
569
  };
570
- if (options.logRawSqlQuery) {
570
+ if (options.logCache) {
571
571
  const q = sql2.toSQL();
572
572
  console.debug(
573
573
  `[forge-sql-orm][local-cache][SAVE] Stored result in cache. sql="${q.sql}", params=${JSON.stringify(q.params)}`
@@ -584,7 +584,7 @@ async function getQueryLocalCacheQuery(query, options) {
584
584
  const sql2 = query;
585
585
  const key = hashKey(sql2.toSQL());
586
586
  if (context.cache[key] && context.cache[key].sql === sql2.toSQL().sql.toLowerCase()) {
587
- if (options.logRawSqlQuery) {
587
+ if (options.logCache) {
588
588
  const q = sql2.toSQL();
589
589
  console.debug(
590
590
  `[forge-sql-orm][local-cache][HIT] Returned cached result. sql="${q.sql}", params=${JSON.stringify(q.params)}`
@@ -1015,6 +1015,18 @@ class ForgeSQLSelectOperations {
1015
1015
  return updateQueryResponseResults.rows;
1016
1016
  }
1017
1017
  }
1018
+ const metadataQueryContext = new AsyncLocalStorage();
1019
+ async function saveMetaDataInContextContext(metadata) {
1020
+ const context = metadataQueryContext.getStore();
1021
+ if (context && metadata) {
1022
+ context.totalResponseSize += metadata.responseSize;
1023
+ context.totalDbExecutionTime += metadata.dbExecutionTime;
1024
+ context.lastMetadata = metadata;
1025
+ }
1026
+ }
1027
+ async function getLastestMetadata() {
1028
+ return metadataQueryContext.getStore();
1029
+ }
1018
1030
  const forgeDriver = async (query, params, method) => {
1019
1031
  if (method == "execute") {
1020
1032
  const sqlStatement = sql$1.prepare(query);
@@ -1030,6 +1042,7 @@ const forgeDriver = async (query, params, method) => {
1030
1042
  await sqlStatement.bindParams(...params);
1031
1043
  }
1032
1044
  const result = await sqlStatement.execute();
1045
+ await saveMetaDataInContextContext(result.metadata);
1033
1046
  let rows;
1034
1047
  rows = result.rows.map((r) => Object.values(r));
1035
1048
  return { rows };
@@ -1779,6 +1792,7 @@ class ForgeSQLORMImpl {
1779
1792
  try {
1780
1793
  const newOptions = options ?? {
1781
1794
  logRawSqlQuery: false,
1795
+ logCache: false,
1782
1796
  disableOptimisticLocking: false,
1783
1797
  cacheWrapTable: true,
1784
1798
  cacheTTL: 120,
@@ -1804,6 +1818,42 @@ class ForgeSQLORMImpl {
1804
1818
  throw error;
1805
1819
  }
1806
1820
  }
1821
+ /**
1822
+ * Executes a query and provides access to execution metadata.
1823
+ * This method allows you to capture detailed information about query execution
1824
+ * including database execution time, response size, and Forge SQL metadata.
1825
+ *
1826
+ * @template T - The return type of the query
1827
+ * @param query - A function that returns a Promise with the query result
1828
+ * @param onMetadata - Callback function that receives execution metadata
1829
+ * @returns Promise with the query result
1830
+ * @example
1831
+ * ```typescript
1832
+ * const result = await forgeSQL.executeWithMetadata(
1833
+ * async () => await forgeSQL.select().from(users).where(eq(users.id, 1)),
1834
+ * (dbTime, responseSize, metadata) => {
1835
+ * console.log(`DB execution time: ${dbTime}ms`);
1836
+ * console.log(`Response size: ${responseSize} bytes`);
1837
+ * console.log('Forge metadata:', metadata);
1838
+ * }
1839
+ * );
1840
+ * ```
1841
+ */
1842
+ async executeWithMetadata(query, onMetadata) {
1843
+ return metadataQueryContext.run({
1844
+ totalDbExecutionTime: 0,
1845
+ totalResponseSize: 0
1846
+ }, async () => {
1847
+ try {
1848
+ return await query();
1849
+ } finally {
1850
+ const metadata = await getLastestMetadata();
1851
+ if (metadata && metadata.lastMetadata) {
1852
+ await onMetadata(metadata.totalDbExecutionTime, metadata.totalResponseSize, metadata.lastMetadata);
1853
+ }
1854
+ }
1855
+ });
1856
+ }
1807
1857
  /**
1808
1858
  * Executes operations within a cache context that collects cache eviction events.
1809
1859
  * All clearCache calls within the context are collected and executed in batch at the end.
@@ -2251,6 +2301,30 @@ class ForgeSQLORM {
2251
2301
  constructor(options) {
2252
2302
  this.ormInstance = ForgeSQLORMImpl.getInstance(options);
2253
2303
  }
2304
+ /**
2305
+ * Executes a query and provides access to execution metadata.
2306
+ * This method allows you to capture detailed information about query execution
2307
+ * including database execution time, response size, and Forge SQL metadata.
2308
+ *
2309
+ * @template T - The return type of the query
2310
+ * @param query - A function that returns a Promise with the query result
2311
+ * @param onMetadata - Callback function that receives execution metadata
2312
+ * @returns Promise with the query result
2313
+ * @example
2314
+ * ```typescript
2315
+ * const result = await forgeSQL.executeWithMetadata(
2316
+ * async () => await forgeSQL.select().from(users).where(eq(users.id, 1)),
2317
+ * (dbTime, responseSize, metadata) => {
2318
+ * console.log(`DB execution time: ${dbTime}ms`);
2319
+ * console.log(`Response size: ${responseSize} bytes`);
2320
+ * console.log('Forge metadata:', metadata);
2321
+ * }
2322
+ * );
2323
+ * ```
2324
+ */
2325
+ async executeWithMetadata(query, onMetadata) {
2326
+ return this.ormInstance.executeWithMetadata(query, onMetadata);
2327
+ }
2254
2328
  selectCacheable(fields, cacheTTL) {
2255
2329
  return this.ormInstance.selectCacheable(fields, cacheTTL);
2256
2330
  }
@@ -2623,6 +2697,435 @@ const migrations = mysqlTable("__migrations", {
2623
2697
  name: varchar("name", { length: 255 }).notNull(),
2624
2698
  migratedAt: timestamp("migratedAt").defaultNow().notNull()
2625
2699
  });
2700
+ const informationSchema = mysqlSchema("information_schema");
2701
+ const slowQuery = informationSchema.table("SLOW_QUERY", {
2702
+ time: timestamp("Time", { fsp: 6, mode: "string" }).notNull(),
2703
+ // Timestamp when the slow query was recorded
2704
+ txnStartTs: bigint("Txn_start_ts", { mode: "bigint", unsigned: true }),
2705
+ // Transaction start timestamp (TSO)
2706
+ user: varchar("User", { length: 64 }),
2707
+ // User executing the query
2708
+ host: varchar("Host", { length: 64 }),
2709
+ // Host from which the query originated
2710
+ connId: bigint("Conn_ID", { mode: "bigint", unsigned: true }),
2711
+ // Connection ID
2712
+ sessionAlias: varchar("Session_alias", { length: 64 }),
2713
+ // Session alias
2714
+ execRetryCount: bigint("Exec_retry_count", { mode: "bigint", unsigned: true }),
2715
+ // Number of retries during execution
2716
+ execRetryTime: double("Exec_retry_time"),
2717
+ // Time spent in retries
2718
+ queryTime: double("Query_time"),
2719
+ // Total execution time
2720
+ parseTime: double("Parse_time"),
2721
+ // Time spent parsing SQL
2722
+ compileTime: double("Compile_time"),
2723
+ // Time spent compiling query plan
2724
+ rewriteTime: double("Rewrite_time"),
2725
+ // Time spent rewriting query
2726
+ preprocSubqueries: bigint("Preproc_subqueries", { mode: "bigint", unsigned: true }),
2727
+ // Number of subqueries preprocessed
2728
+ preprocSubqueriesTime: double("Preproc_subqueries_time"),
2729
+ // Time spent preprocessing subqueries
2730
+ optimizeTime: double("Optimize_time"),
2731
+ // Time spent in optimizer
2732
+ waitTs: double("Wait_TS"),
2733
+ // Wait time for getting TSO
2734
+ prewriteTime: double("Prewrite_time"),
2735
+ // Time spent in prewrite phase
2736
+ waitPrewriteBinlogTime: double("Wait_prewrite_binlog_time"),
2737
+ // Time waiting for binlog prewrite
2738
+ commitTime: double("Commit_time"),
2739
+ // Commit duration
2740
+ getCommitTsTime: double("Get_commit_ts_time"),
2741
+ // Time waiting for commit TSO
2742
+ commitBackoffTime: double("Commit_backoff_time"),
2743
+ // Backoff time during commit
2744
+ backoffTypes: varchar("Backoff_types", { length: 64 }),
2745
+ // Types of backoff occurred
2746
+ resolveLockTime: double("Resolve_lock_time"),
2747
+ // Time resolving locks
2748
+ localLatchWaitTime: double("Local_latch_wait_time"),
2749
+ // Time waiting on local latch
2750
+ writeKeys: bigint("Write_keys", { mode: "bigint" }),
2751
+ // Number of keys written
2752
+ writeSize: bigint("Write_size", { mode: "bigint" }),
2753
+ // Amount of data written
2754
+ prewriteRegion: bigint("Prewrite_region", { mode: "bigint" }),
2755
+ // Regions involved in prewrite
2756
+ txnRetry: bigint("Txn_retry", { mode: "bigint" }),
2757
+ // Transaction retry count
2758
+ copTime: double("Cop_time"),
2759
+ // Time spent in coprocessor
2760
+ processTime: double("Process_time"),
2761
+ // Processing time
2762
+ waitTime: double("Wait_time"),
2763
+ // Wait time in TiKV
2764
+ backoffTime: double("Backoff_time"),
2765
+ // Backoff wait time
2766
+ lockKeysTime: double("LockKeys_time"),
2767
+ // Time spent waiting for locks
2768
+ requestCount: bigint("Request_count", { mode: "bigint", unsigned: true }),
2769
+ // Total number of requests
2770
+ totalKeys: bigint("Total_keys", { mode: "bigint", unsigned: true }),
2771
+ // Total keys scanned
2772
+ processKeys: bigint("Process_keys", { mode: "bigint", unsigned: true }),
2773
+ // Keys processed
2774
+ rocksdbDeleteSkippedCount: bigint("Rocksdb_delete_skipped_count", {
2775
+ mode: "bigint",
2776
+ unsigned: true
2777
+ }),
2778
+ // RocksDB delete skips
2779
+ rocksdbKeySkippedCount: bigint("Rocksdb_key_skipped_count", { mode: "bigint", unsigned: true }),
2780
+ // RocksDB key skips
2781
+ rocksdbBlockCacheHitCount: bigint("Rocksdb_block_cache_hit_count", {
2782
+ mode: "bigint",
2783
+ unsigned: true
2784
+ }),
2785
+ // RocksDB block cache hits
2786
+ rocksdbBlockReadCount: bigint("Rocksdb_block_read_count", { mode: "bigint", unsigned: true }),
2787
+ // RocksDB block reads
2788
+ rocksdbBlockReadByte: bigint("Rocksdb_block_read_byte", { mode: "bigint", unsigned: true }),
2789
+ // RocksDB block read bytes
2790
+ db: varchar("DB", { length: 64 }),
2791
+ // Database name
2792
+ indexNames: varchar("Index_names", { length: 100 }),
2793
+ // Indexes used
2794
+ isInternal: boolean("Is_internal"),
2795
+ // Whether the query is internal
2796
+ digest: varchar("Digest", { length: 64 }),
2797
+ // SQL digest hash
2798
+ stats: varchar("Stats", { length: 512 }),
2799
+ // Stats used during planning
2800
+ copProcAvg: double("Cop_proc_avg"),
2801
+ // Coprocessor average processing time
2802
+ copProcP90: double("Cop_proc_p90"),
2803
+ // Coprocessor 90th percentile processing time
2804
+ copProcMax: double("Cop_proc_max"),
2805
+ // Coprocessor max processing time
2806
+ copProcAddr: varchar("Cop_proc_addr", { length: 64 }),
2807
+ // Coprocessor address for processing
2808
+ copWaitAvg: double("Cop_wait_avg"),
2809
+ // Coprocessor average wait time
2810
+ copWaitP90: double("Cop_wait_p90"),
2811
+ // Coprocessor 90th percentile wait time
2812
+ copWaitMax: double("Cop_wait_max"),
2813
+ // Coprocessor max wait time
2814
+ copWaitAddr: varchar("Cop_wait_addr", { length: 64 }),
2815
+ // Coprocessor address for wait
2816
+ memMax: bigint("Mem_max", { mode: "bigint" }),
2817
+ // Max memory usage
2818
+ diskMax: bigint("Disk_max", { mode: "bigint" }),
2819
+ // Max disk usage
2820
+ kvTotal: double("KV_total"),
2821
+ // Total KV request time
2822
+ pdTotal: double("PD_total"),
2823
+ // Total PD request time
2824
+ backoffTotal: double("Backoff_total"),
2825
+ // Total backoff time
2826
+ writeSqlResponseTotal: double("Write_sql_response_total"),
2827
+ // SQL response write time
2828
+ resultRows: bigint("Result_rows", { mode: "bigint" }),
2829
+ // Rows returned
2830
+ warnings: longtext("Warnings"),
2831
+ // Warnings during execution
2832
+ backoffDetail: varchar("Backoff_Detail", { length: 4096 }),
2833
+ // Detailed backoff info
2834
+ prepared: boolean("Prepared"),
2835
+ // Whether query was prepared
2836
+ succ: boolean("Succ"),
2837
+ // Success flag
2838
+ isExplicitTxn: boolean("IsExplicitTxn"),
2839
+ // Whether explicit transaction
2840
+ isWriteCacheTable: boolean("IsWriteCacheTable"),
2841
+ // Whether wrote to cache table
2842
+ planFromCache: boolean("Plan_from_cache"),
2843
+ // Plan was from cache
2844
+ planFromBinding: boolean("Plan_from_binding"),
2845
+ // Plan was from binding
2846
+ hasMoreResults: boolean("Has_more_results"),
2847
+ // Query returned multiple results
2848
+ resourceGroup: varchar("Resource_group", { length: 64 }),
2849
+ // Resource group name
2850
+ requestUnitRead: double("Request_unit_read"),
2851
+ // RU consumed for read
2852
+ requestUnitWrite: double("Request_unit_write"),
2853
+ // RU consumed for write
2854
+ timeQueuedByRc: double("Time_queued_by_rc"),
2855
+ // Time queued by resource control
2856
+ tidbCpuTime: double("Tidb_cpu_time"),
2857
+ // TiDB CPU time
2858
+ tikvCpuTime: double("Tikv_cpu_time"),
2859
+ // TiKV CPU time
2860
+ plan: longtext("Plan"),
2861
+ // Query execution plan
2862
+ planDigest: varchar("Plan_digest", { length: 128 }),
2863
+ // Plan digest hash
2864
+ binaryPlan: longtext("Binary_plan"),
2865
+ // Binary execution plan
2866
+ prevStmt: longtext("Prev_stmt"),
2867
+ // Previous statement in session
2868
+ query: longtext("Query")
2869
+ // Original SQL query
2870
+ });
2871
+ const createClusterStatementsSummarySchema = () => ({
2872
+ instance: varchar("INSTANCE", { length: 64 }),
2873
+ // TiDB/TiKV instance address
2874
+ summaryBeginTime: timestamp("SUMMARY_BEGIN_TIME", { mode: "string" }).notNull(),
2875
+ // Begin time of this summary window
2876
+ summaryEndTime: timestamp("SUMMARY_END_TIME", { mode: "string" }).notNull(),
2877
+ // End time of this summary window
2878
+ stmtType: varchar("STMT_TYPE", { length: 64 }).notNull(),
2879
+ // Statement type (e.g., Select/Insert/Update)
2880
+ schemaName: varchar("SCHEMA_NAME", { length: 64 }),
2881
+ // Current schema name
2882
+ digest: varchar("DIGEST", { length: 64 }),
2883
+ // SQL digest (normalized hash)
2884
+ digestText: text("DIGEST_TEXT").notNull(),
2885
+ // Normalized SQL text
2886
+ tableNames: text("TABLE_NAMES"),
2887
+ // Involved table names
2888
+ indexNames: text("INDEX_NAMES"),
2889
+ // Used index names
2890
+ sampleUser: varchar("SAMPLE_USER", { length: 64 }),
2891
+ // Sampled user who executed the statements
2892
+ execCount: bigint("EXEC_COUNT", { mode: "bigint", unsigned: true }).notNull(),
2893
+ // Total executions
2894
+ sumErrors: int("SUM_ERRORS", { unsigned: true }).notNull(),
2895
+ // Sum of errors
2896
+ sumWarnings: int("SUM_WARNINGS", { unsigned: true }).notNull(),
2897
+ // Sum of warnings
2898
+ sumLatency: bigint("SUM_LATENCY", { mode: "bigint", unsigned: true }).notNull(),
2899
+ // Sum of latency (ns)
2900
+ maxLatency: bigint("MAX_LATENCY", { mode: "bigint", unsigned: true }).notNull(),
2901
+ // Max latency (ns)
2902
+ minLatency: bigint("MIN_LATENCY", { mode: "bigint", unsigned: true }).notNull(),
2903
+ // Min latency (ns)
2904
+ avgLatency: bigint("AVG_LATENCY", { mode: "bigint", unsigned: true }).notNull(),
2905
+ // Avg latency (ns)
2906
+ avgParseLatency: bigint("AVG_PARSE_LATENCY", { mode: "bigint", unsigned: true }).notNull(),
2907
+ // Avg parse time (ns)
2908
+ maxParseLatency: bigint("MAX_PARSE_LATENCY", { mode: "bigint", unsigned: true }).notNull(),
2909
+ // Max parse time (ns)
2910
+ avgCompileLatency: bigint("AVG_COMPILE_LATENCY", { mode: "bigint", unsigned: true }).notNull(),
2911
+ // Avg compile time (ns)
2912
+ maxCompileLatency: bigint("MAX_COMPILE_LATENCY", { mode: "bigint", unsigned: true }).notNull(),
2913
+ // Max compile time (ns)
2914
+ sumCopTaskNum: bigint("SUM_COP_TASK_NUM", { mode: "bigint", unsigned: true }).notNull(),
2915
+ // Total number of cop tasks
2916
+ maxCopProcessTime: bigint("MAX_COP_PROCESS_TIME", { mode: "bigint", unsigned: true }).notNull(),
2917
+ // Max TiKV coprocessor processing time (ns)
2918
+ maxCopProcessAddress: varchar("MAX_COP_PROCESS_ADDRESS", { length: 256 }),
2919
+ // Address of cop task with max processing time
2920
+ maxCopWaitTime: bigint("MAX_COP_WAIT_TIME", { mode: "bigint", unsigned: true }).notNull(),
2921
+ // Max TiKV coprocessor wait time (ns)
2922
+ maxCopWaitAddress: varchar("MAX_COP_WAIT_ADDRESS", { length: 256 }),
2923
+ // Address of cop task with max wait time
2924
+ avgProcessTime: bigint("AVG_PROCESS_TIME", { mode: "bigint", unsigned: true }).notNull(),
2925
+ // Avg TiKV processing time (ns)
2926
+ maxProcessTime: bigint("MAX_PROCESS_TIME", { mode: "bigint", unsigned: true }).notNull(),
2927
+ // Max TiKV processing time (ns)
2928
+ avgWaitTime: bigint("AVG_WAIT_TIME", { mode: "bigint", unsigned: true }).notNull(),
2929
+ // Avg TiKV wait time (ns)
2930
+ maxWaitTime: bigint("MAX_WAIT_TIME", { mode: "bigint", unsigned: true }).notNull(),
2931
+ // Max TiKV wait time (ns)
2932
+ avgBackoffTime: bigint("AVG_BACKOFF_TIME", { mode: "bigint", unsigned: true }).notNull(),
2933
+ // Avg backoff time before retry (ns)
2934
+ maxBackoffTime: bigint("MAX_BACKOFF_TIME", { mode: "bigint", unsigned: true }).notNull(),
2935
+ // Max backoff time before retry (ns)
2936
+ avgTotalKeys: bigint("AVG_TOTAL_KEYS", { mode: "bigint", unsigned: true }).notNull(),
2937
+ // Avg scanned keys
2938
+ maxTotalKeys: bigint("MAX_TOTAL_KEYS", { mode: "bigint", unsigned: true }).notNull(),
2939
+ // Max scanned keys
2940
+ avgProcessedKeys: bigint("AVG_PROCESSED_KEYS", { mode: "bigint", unsigned: true }).notNull(),
2941
+ // Avg processed keys
2942
+ maxProcessedKeys: bigint("MAX_PROCESSED_KEYS", { mode: "bigint", unsigned: true }).notNull(),
2943
+ // Max processed keys
2944
+ avgRocksdbDeleteSkippedCount: double("AVG_ROCKSDB_DELETE_SKIPPED_COUNT").notNull(),
2945
+ // Avg RocksDB deletes skipped
2946
+ maxRocksdbDeleteSkippedCount: int("MAX_ROCKSDB_DELETE_SKIPPED_COUNT", {
2947
+ unsigned: true
2948
+ }).notNull(),
2949
+ // Max RocksDB deletes skipped
2950
+ avgRocksdbKeySkippedCount: double("AVG_ROCKSDB_KEY_SKIPPED_COUNT").notNull(),
2951
+ // Avg RocksDB keys skipped
2952
+ maxRocksdbKeySkippedCount: int("MAX_ROCKSDB_KEY_SKIPPED_COUNT", { unsigned: true }).notNull(),
2953
+ // Max RocksDB keys skipped
2954
+ avgRocksdbBlockCacheHitCount: double("AVG_ROCKSDB_BLOCK_CACHE_HIT_COUNT").notNull(),
2955
+ // Avg RocksDB block cache hits
2956
+ maxRocksdbBlockCacheHitCount: int("MAX_ROCKSDB_BLOCK_CACHE_HIT_COUNT", {
2957
+ unsigned: true
2958
+ }).notNull(),
2959
+ // Max RocksDB block cache hits
2960
+ avgRocksdbBlockReadCount: double("AVG_ROCKSDB_BLOCK_READ_COUNT").notNull(),
2961
+ // Avg RocksDB block reads
2962
+ maxRocksdbBlockReadCount: int("MAX_ROCKSDB_BLOCK_READ_COUNT", { unsigned: true }).notNull(),
2963
+ // Max RocksDB block reads
2964
+ avgRocksdbBlockReadByte: double("AVG_ROCKSDB_BLOCK_READ_BYTE").notNull(),
2965
+ // Avg RocksDB block read bytes
2966
+ maxRocksdbBlockReadByte: int("MAX_ROCKSDB_BLOCK_READ_BYTE", { unsigned: true }).notNull(),
2967
+ // Max RocksDB block read bytes
2968
+ avgPrewriteTime: bigint("AVG_PREWRITE_TIME", { mode: "bigint", unsigned: true }).notNull(),
2969
+ // Avg prewrite phase time (ns)
2970
+ maxPrewriteTime: bigint("MAX_PREWRITE_TIME", { mode: "bigint", unsigned: true }).notNull(),
2971
+ // Max prewrite phase time (ns)
2972
+ avgCommitTime: bigint("AVG_COMMIT_TIME", { mode: "bigint", unsigned: true }).notNull(),
2973
+ // Avg commit phase time (ns)
2974
+ maxCommitTime: bigint("MAX_COMMIT_TIME", { mode: "bigint", unsigned: true }).notNull(),
2975
+ // Max commit phase time (ns)
2976
+ avgGetCommitTsTime: bigint("AVG_GET_COMMIT_TS_TIME", {
2977
+ mode: "bigint",
2978
+ unsigned: true
2979
+ }).notNull(),
2980
+ // Avg get commit_ts time (ns)
2981
+ maxGetCommitTsTime: bigint("MAX_GET_COMMIT_TS_TIME", {
2982
+ mode: "bigint",
2983
+ unsigned: true
2984
+ }).notNull(),
2985
+ // Max get commit_ts time (ns)
2986
+ avgCommitBackoffTime: bigint("AVG_COMMIT_BACKOFF_TIME", {
2987
+ mode: "bigint",
2988
+ unsigned: true
2989
+ }).notNull(),
2990
+ // Avg backoff during commit (ns)
2991
+ maxCommitBackoffTime: bigint("MAX_COMMIT_BACKOFF_TIME", {
2992
+ mode: "bigint",
2993
+ unsigned: true
2994
+ }).notNull(),
2995
+ // Max backoff during commit (ns)
2996
+ avgResolveLockTime: bigint("AVG_RESOLVE_LOCK_TIME", {
2997
+ mode: "bigint",
2998
+ unsigned: true
2999
+ }).notNull(),
3000
+ // Avg resolve lock time (ns)
3001
+ maxResolveLockTime: bigint("MAX_RESOLVE_LOCK_TIME", {
3002
+ mode: "bigint",
3003
+ unsigned: true
3004
+ }).notNull(),
3005
+ // Max resolve lock time (ns)
3006
+ avgLocalLatchWaitTime: bigint("AVG_LOCAL_LATCH_WAIT_TIME", {
3007
+ mode: "bigint",
3008
+ unsigned: true
3009
+ }).notNull(),
3010
+ // Avg local latch wait (ns)
3011
+ maxLocalLatchWaitTime: bigint("MAX_LOCAL_LATCH_WAIT_TIME", {
3012
+ mode: "bigint",
3013
+ unsigned: true
3014
+ }).notNull(),
3015
+ // Max local latch wait (ns)
3016
+ avgWriteKeys: double("AVG_WRITE_KEYS").notNull(),
3017
+ // Avg number of written keys
3018
+ maxWriteKeys: bigint("MAX_WRITE_KEYS", { mode: "bigint", unsigned: true }).notNull(),
3019
+ // Max written keys
3020
+ avgWriteSize: double("AVG_WRITE_SIZE").notNull(),
3021
+ // Avg written bytes
3022
+ maxWriteSize: bigint("MAX_WRITE_SIZE", { mode: "bigint", unsigned: true }).notNull(),
3023
+ // Max written bytes
3024
+ avgPrewriteRegions: double("AVG_PREWRITE_REGIONS").notNull(),
3025
+ // Avg regions in prewrite
3026
+ maxPrewriteRegions: int("MAX_PREWRITE_REGIONS", { unsigned: true }).notNull(),
3027
+ // Max regions in prewrite
3028
+ avgTxnRetry: double("AVG_TXN_RETRY").notNull(),
3029
+ // Avg transaction retry count
3030
+ maxTxnRetry: int("MAX_TXN_RETRY", { unsigned: true }).notNull(),
3031
+ // Max transaction retry count
3032
+ sumExecRetry: bigint("SUM_EXEC_RETRY", { mode: "bigint", unsigned: true }).notNull(),
3033
+ // Sum of execution retries (pessimistic)
3034
+ sumExecRetryTime: bigint("SUM_EXEC_RETRY_TIME", { mode: "bigint", unsigned: true }).notNull(),
3035
+ // Sum time of execution retries (ns)
3036
+ sumBackoffTimes: bigint("SUM_BACKOFF_TIMES", { mode: "bigint", unsigned: true }).notNull(),
3037
+ // Sum of backoff retries
3038
+ backoffTypes: varchar("BACKOFF_TYPES", { length: 1024 }),
3039
+ // Backoff types with counts
3040
+ avgMem: bigint("AVG_MEM", { mode: "bigint", unsigned: true }).notNull(),
3041
+ // Avg memory used (bytes)
3042
+ maxMem: bigint("MAX_MEM", { mode: "bigint", unsigned: true }).notNull(),
3043
+ // Max memory used (bytes)
3044
+ avgDisk: bigint("AVG_DISK", { mode: "bigint", unsigned: true }).notNull(),
3045
+ // Avg disk used (bytes)
3046
+ maxDisk: bigint("MAX_DISK", { mode: "bigint", unsigned: true }).notNull(),
3047
+ // Max disk used (bytes)
3048
+ avgKvTime: bigint("AVG_KV_TIME", { mode: "bigint", unsigned: true }).notNull(),
3049
+ // Avg time spent in TiKV (ns)
3050
+ avgPdTime: bigint("AVG_PD_TIME", { mode: "bigint", unsigned: true }).notNull(),
3051
+ // Avg time spent in PD (ns)
3052
+ avgBackoffTotalTime: bigint("AVG_BACKOFF_TOTAL_TIME", {
3053
+ mode: "bigint",
3054
+ unsigned: true
3055
+ }).notNull(),
3056
+ // Avg total backoff time (ns)
3057
+ avgWriteSqlRespTime: bigint("AVG_WRITE_SQL_RESP_TIME", {
3058
+ mode: "bigint",
3059
+ unsigned: true
3060
+ }).notNull(),
3061
+ // Avg write SQL response time (ns)
3062
+ avgTidbCpuTime: bigint("AVG_TIDB_CPU_TIME", { mode: "bigint", unsigned: true }).notNull(),
3063
+ // Avg TiDB CPU time (ns)
3064
+ avgTikvCpuTime: bigint("AVG_TIKV_CPU_TIME", { mode: "bigint", unsigned: true }).notNull(),
3065
+ // Avg TiKV CPU time (ns)
3066
+ maxResultRows: bigint("MAX_RESULT_ROWS", { mode: "bigint" }).notNull(),
3067
+ // Max number of result rows
3068
+ minResultRows: bigint("MIN_RESULT_ROWS", { mode: "bigint" }).notNull(),
3069
+ // Min number of result rows
3070
+ avgResultRows: bigint("AVG_RESULT_ROWS", { mode: "bigint" }).notNull(),
3071
+ // Avg number of result rows
3072
+ prepared: boolean("PREPARED").notNull(),
3073
+ // Whether statements are prepared
3074
+ avgAffectedRows: double("AVG_AFFECTED_ROWS").notNull(),
3075
+ // Avg affected rows
3076
+ firstSeen: timestamp("FIRST_SEEN", { mode: "string" }).notNull(),
3077
+ // First time statements observed
3078
+ lastSeen: timestamp("LAST_SEEN", { mode: "string" }).notNull(),
3079
+ // Last time statements observed
3080
+ planInCache: boolean("PLAN_IN_CACHE").notNull(),
3081
+ // Whether last stmt hit plan cache
3082
+ planCacheHits: bigint("PLAN_CACHE_HITS", { mode: "bigint" }).notNull(),
3083
+ // Number of plan cache hits
3084
+ planInBinding: boolean("PLAN_IN_BINDING").notNull(),
3085
+ // Whether matched bindings
3086
+ querySampleText: text("QUERY_SAMPLE_TEXT"),
3087
+ // Sampled original SQL
3088
+ prevSampleText: text("PREV_SAMPLE_TEXT"),
3089
+ // Sampled previous SQL before commit
3090
+ planDigest: varchar("PLAN_DIGEST", { length: 64 }),
3091
+ // Plan digest hash
3092
+ plan: text("PLAN"),
3093
+ // Sampled textual plan
3094
+ binaryPlan: text("BINARY_PLAN"),
3095
+ // Sampled binary plan
3096
+ charset: varchar("CHARSET", { length: 64 }),
3097
+ // Sampled charset
3098
+ collation: varchar("COLLATION", { length: 64 }),
3099
+ // Sampled collation
3100
+ planHint: varchar("PLAN_HINT", { length: 64 }),
3101
+ // Sampled plan hint
3102
+ maxRequestUnitRead: double("MAX_REQUEST_UNIT_READ").notNull(),
3103
+ // Max RU cost (read)
3104
+ avgRequestUnitRead: double("AVG_REQUEST_UNIT_READ").notNull(),
3105
+ // Avg RU cost (read)
3106
+ maxRequestUnitWrite: double("MAX_REQUEST_UNIT_WRITE").notNull(),
3107
+ // Max RU cost (write)
3108
+ avgRequestUnitWrite: double("AVG_REQUEST_UNIT_WRITE").notNull(),
3109
+ // Avg RU cost (write)
3110
+ maxQueuedRcTime: bigint("MAX_QUEUED_RC_TIME", { mode: "bigint", unsigned: true }).notNull(),
3111
+ // Max queued time waiting for RU (ns)
3112
+ avgQueuedRcTime: bigint("AVG_QUEUED_RC_TIME", { mode: "bigint", unsigned: true }).notNull(),
3113
+ // Avg queued time waiting for RU (ns)
3114
+ resourceGroup: varchar("RESOURCE_GROUP", { length: 64 }),
3115
+ // Bound resource group name
3116
+ planCacheUnqualified: bigint("PLAN_CACHE_UNQUALIFIED", { mode: "bigint" }).notNull(),
3117
+ // Times not eligible for plan cache
3118
+ planCacheUnqualifiedLastReason: text("PLAN_CACHE_UNQUALIFIED_LAST_REASON")
3119
+ // Last reason of plan cache ineligibility
3120
+ });
3121
+ const clusterStatementsSummaryHistory = informationSchema.table(
3122
+ "CLUSTER_STATEMENTS_SUMMARY_HISTORY",
3123
+ createClusterStatementsSummarySchema()
3124
+ );
3125
+ const clusterStatementsSummary = informationSchema.table(
3126
+ "CLUSTER_STATEMENTS_SUMMARY",
3127
+ createClusterStatementsSummarySchema()
3128
+ );
2626
3129
  async function getTables() {
2627
3130
  const tables = await sql$1.executeDDL("SHOW TABLES");
2628
3131
  return tables.rows.flatMap((tableInfo) => Object.values(tableInfo));
@@ -2673,13 +3176,13 @@ const applySchemaMigrations = async (migration) => {
2673
3176
  body: "Migrations successfully executed"
2674
3177
  };
2675
3178
  } catch (error) {
2676
- const errorMessage = error?.debug?.sqlMessage ?? error?.debug?.message ?? error.message ?? "Unknown error occurred";
3179
+ const errorMessage = error?.cause?.context?.debug?.sqlMessage ?? error?.cause?.context?.debug?.message ?? error?.debug?.context?.sqlMessage ?? error?.debug?.context?.message ?? error.message ?? "Unknown error occurred";
2677
3180
  console.error("Error during migration:", errorMessage);
2678
3181
  return {
2679
3182
  headers: { "Content-Type": ["application/json"] },
2680
3183
  statusCode: 500,
2681
3184
  statusText: "Internal Server Error",
2682
- body: error instanceof Error ? error.message : "Unknown error during migration"
3185
+ body: error instanceof Error ? errorMessage : "Unknown error during migration"
2683
3186
  };
2684
3187
  }
2685
3188
  };
@@ -2770,6 +3273,200 @@ const clearCacheSchedulerTrigger = async (options) => {
2770
3273
  };
2771
3274
  }
2772
3275
  };
3276
+ const DEFAULT_MEMORY_THRESHOLD = 8 * 1024 * 1024;
3277
+ const DEFAULT_TIMEOUT = 300;
3278
+ const topSlowestStatementLastHourTrigger = async (orm, options) => {
3279
+ if (!orm) {
3280
+ return {
3281
+ statusCode: 500,
3282
+ headers: { "Content-Type": ["application/json"] },
3283
+ body: JSON.stringify({
3284
+ success: false,
3285
+ message: "ORM instance is required",
3286
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3287
+ })
3288
+ };
3289
+ }
3290
+ let newOptions = options ?? {
3291
+ warnThresholdMs: DEFAULT_TIMEOUT,
3292
+ memoryThresholdBytes: DEFAULT_MEMORY_THRESHOLD,
3293
+ showPlan: false
3294
+ };
3295
+ const nsToMs = (v) => {
3296
+ const n = Number(v);
3297
+ return Number.isFinite(n) ? n / 1e6 : NaN;
3298
+ };
3299
+ const bytesToMB = (v) => {
3300
+ const n = Number(v);
3301
+ return Number.isFinite(n) ? n / (1024 * 1024) : NaN;
3302
+ };
3303
+ const jsonSafeStringify = (value) => JSON.stringify(value, (_k, v) => typeof v === "bigint" ? v.toString() : v);
3304
+ function sanitizeSQL(sql2, maxLen = 1e3) {
3305
+ let s = sql2;
3306
+ s = s.replace(/--[^\n\r]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
3307
+ s = s.replace(/'(?:\\'|[^'])*'/g, "?");
3308
+ s = s.replace(/\b-?\d+(?:\.\d+)?\b/g, "?");
3309
+ s = s.replace(/\s+/g, " ").trim();
3310
+ if (s.length > maxLen) {
3311
+ s = s.slice(0, maxLen) + " …[truncated]";
3312
+ }
3313
+ return s;
3314
+ }
3315
+ const TOP_N = 1;
3316
+ try {
3317
+ const summaryHistory = clusterStatementsSummaryHistory;
3318
+ const summary = clusterStatementsSummary;
3319
+ const selectShape = (t) => ({
3320
+ digest: t.digest,
3321
+ stmtType: t.stmtType,
3322
+ schemaName: t.schemaName,
3323
+ execCount: t.execCount,
3324
+ avgLatencyNs: t.avgLatency,
3325
+ maxLatencyNs: t.maxLatency,
3326
+ minLatencyNs: t.minLatency,
3327
+ avgProcessTimeNs: t.avgProcessTime,
3328
+ avgWaitTimeNs: t.avgWaitTime,
3329
+ avgBackoffTimeNs: t.avgBackoffTime,
3330
+ avgTotalKeys: t.avgTotalKeys,
3331
+ firstSeen: t.firstSeen,
3332
+ lastSeen: t.lastSeen,
3333
+ planInCache: t.planInCache,
3334
+ planCacheHits: t.planCacheHits,
3335
+ digestText: t.digestText,
3336
+ plan: t.plan,
3337
+ avgMemBytes: t.avgMem,
3338
+ maxMemBytes: t.maxMem
3339
+ });
3340
+ const lastHourFilterHistory = gte(
3341
+ summaryHistory.summaryEndTime,
3342
+ sql`DATE_SUB(NOW(), INTERVAL 1 HOUR)`
3343
+ );
3344
+ const lastHourFilterSummary = gte(
3345
+ summary.summaryEndTime,
3346
+ sql`DATE_SUB(NOW(), INTERVAL 1 HOUR)`
3347
+ );
3348
+ const qHistory = orm.getDrizzleQueryBuilder().select(selectShape(summaryHistory)).from(summaryHistory).where(lastHourFilterHistory);
3349
+ const qSummary = orm.getDrizzleQueryBuilder().select(selectShape(summary)).from(summary).where(lastHourFilterSummary);
3350
+ const combined = unionAll(qHistory, qSummary).as("combined");
3351
+ const thresholdNs = Math.floor((newOptions.warnThresholdMs ?? DEFAULT_TIMEOUT) * 1e6);
3352
+ const memoryThresholdBytes = newOptions.memoryThresholdBytes ?? DEFAULT_MEMORY_THRESHOLD;
3353
+ const grouped = orm.getDrizzleQueryBuilder().select({
3354
+ digest: combined.digest,
3355
+ stmtType: combined.stmtType,
3356
+ schemaName: combined.schemaName,
3357
+ execCount: sql`SUM(${combined.execCount})`.as("execCount"),
3358
+ avgLatencyNs: sql`MAX(${combined.avgLatencyNs})`.as("avgLatencyNs"),
3359
+ maxLatencyNs: sql`MAX(${combined.maxLatencyNs})`.as("maxLatencyNs"),
3360
+ minLatencyNs: sql`MIN(${combined.minLatencyNs})`.as("minLatencyNs"),
3361
+ avgProcessTimeNs: sql`MAX(${combined.avgProcessTimeNs})`.as("avgProcessTimeNs"),
3362
+ avgWaitTimeNs: sql`MAX(${combined.avgWaitTimeNs})`.as("avgWaitTimeNs"),
3363
+ avgBackoffTimeNs: sql`MAX(${combined.avgBackoffTimeNs})`.as("avgBackoffTimeNs"),
3364
+ avgMemBytes: sql`MAX(${combined.avgMemBytes})`.as("avgMemBytes"),
3365
+ maxMemBytes: sql`MAX(${combined.maxMemBytes})`.as("maxMemBytes"),
3366
+ avgTotalKeys: sql`MAX(${combined.avgTotalKeys})`.as("avgTotalKeys"),
3367
+ firstSeen: sql`MIN(${combined.firstSeen})`.as("firstSeen"),
3368
+ lastSeen: sql`MAX(${combined.lastSeen})`.as("lastSeen"),
3369
+ planInCache: sql`MAX(${combined.planInCache})`.as("planInCache"),
3370
+ planCacheHits: sql`SUM(${combined.planCacheHits})`.as("planCacheHits"),
3371
+ // Prefer a non-empty sample text/plan via MAX; acceptable for de-dup
3372
+ digestText: sql`MAX(${combined.digestText})`.as("digestText"),
3373
+ plan: sql`MAX(${combined.plan})`.as("plan")
3374
+ }).from(combined).where(
3375
+ sql`COALESCE(${combined.digest}, '') <> '' AND COALESCE(${combined.digestText}, '') <> '' AND COALESCE(${combined.stmtType}, '') NOT IN ('Use','Set','Show')`
3376
+ ).groupBy(combined.digest, combined.stmtType, combined.schemaName).as("grouped");
3377
+ const rows = await orm.getDrizzleQueryBuilder().select({
3378
+ digest: grouped.digest,
3379
+ stmtType: grouped.stmtType,
3380
+ schemaName: grouped.schemaName,
3381
+ execCount: grouped.execCount,
3382
+ avgLatencyNs: grouped.avgLatencyNs,
3383
+ maxLatencyNs: grouped.maxLatencyNs,
3384
+ minLatencyNs: grouped.minLatencyNs,
3385
+ avgProcessTimeNs: grouped.avgProcessTimeNs,
3386
+ avgWaitTimeNs: grouped.avgWaitTimeNs,
3387
+ avgBackoffTimeNs: grouped.avgBackoffTimeNs,
3388
+ avgMemBytes: grouped.avgMemBytes,
3389
+ maxMemBytes: grouped.maxMemBytes,
3390
+ avgTotalKeys: grouped.avgTotalKeys,
3391
+ firstSeen: grouped.firstSeen,
3392
+ lastSeen: grouped.lastSeen,
3393
+ planInCache: grouped.planInCache,
3394
+ planCacheHits: grouped.planCacheHits,
3395
+ digestText: grouped.digestText,
3396
+ plan: grouped.plan
3397
+ }).from(grouped).where(
3398
+ sql`${grouped.avgLatencyNs} > ${thresholdNs} OR ${grouped.avgMemBytes} > ${memoryThresholdBytes}`
3399
+ ).orderBy(desc(grouped.avgLatencyNs)).limit(formatLimitOffset(TOP_N));
3400
+ const formatted = rows.map((r, i) => ({
3401
+ rank: i + 1,
3402
+ // 1-based rank in the top N
3403
+ digest: r.digest,
3404
+ stmtType: r.stmtType,
3405
+ schemaName: r.schemaName,
3406
+ execCount: r.execCount,
3407
+ avgLatencyMs: nsToMs(r.avgLatencyNs),
3408
+ // Convert ns to ms for readability
3409
+ maxLatencyMs: nsToMs(r.maxLatencyNs),
3410
+ minLatencyMs: nsToMs(r.minLatencyNs),
3411
+ avgProcessTimeMs: nsToMs(r.avgProcessTimeNs),
3412
+ avgWaitTimeMs: nsToMs(r.avgWaitTimeNs),
3413
+ avgBackoffTimeMs: nsToMs(r.avgBackoffTimeNs),
3414
+ avgMemMB: bytesToMB(r.avgMemBytes),
3415
+ maxMemMB: bytesToMB(r.maxMemBytes),
3416
+ avgMemBytes: r.avgMemBytes,
3417
+ maxMemBytes: r.maxMemBytes,
3418
+ avgTotalKeys: r.avgTotalKeys,
3419
+ firstSeen: r.firstSeen,
3420
+ lastSeen: r.lastSeen,
3421
+ planInCache: r.planInCache,
3422
+ planCacheHits: r.planCacheHits,
3423
+ digestText: sanitizeSQL(r.digestText),
3424
+ plan: newOptions.showPlan ? r.plan : void 0
3425
+ }));
3426
+ for (const f of formatted) {
3427
+ console.warn(
3428
+ `${f.rank}. ${f.stmtType} avg=${f.avgLatencyMs?.toFixed?.(2)}ms max=${f.maxLatencyMs?.toFixed?.(2)}ms mem≈${f.avgMemMB?.toFixed?.(2)}MB(max ${f.maxMemMB?.toFixed?.(2)}MB) exec=${f.execCount}
3429
+ digest=${f.digest}
3430
+ sql=${(f.digestText || "").slice(0, 300)}${f.digestText && f.digestText.length > 300 ? "…" : ""}`
3431
+ );
3432
+ if (newOptions.showPlan && f.plan) {
3433
+ console.warn(` full plan:
3434
+ ${f.plan}`);
3435
+ }
3436
+ }
3437
+ return {
3438
+ headers: { "Content-Type": ["application/json"] },
3439
+ statusCode: 200,
3440
+ statusText: "OK",
3441
+ body: jsonSafeStringify({
3442
+ success: true,
3443
+ window: "last_1h",
3444
+ top: TOP_N,
3445
+ warnThresholdMs: newOptions.warnThresholdMs,
3446
+ memoryThresholdBytes: newOptions.memoryThresholdBytes,
3447
+ showPlan: newOptions.showPlan,
3448
+ rows: formatted,
3449
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
3450
+ })
3451
+ };
3452
+ } catch (error) {
3453
+ console.error(
3454
+ "Error in topSlowestStatementLastHourTrigger:",
3455
+ error?.cause?.context?.debug?.sqlMessage ?? error?.cause ?? error
3456
+ );
3457
+ return {
3458
+ headers: { "Content-Type": ["application/json"] },
3459
+ statusCode: 500,
3460
+ statusText: "Internal Server Error",
3461
+ body: jsonSafeStringify({
3462
+ success: false,
3463
+ message: "Failed to fetch or log slow queries",
3464
+ error: error?.cause?.context?.debug?.sqlMessage ?? error?.cause?.message,
3465
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3466
+ })
3467
+ };
3468
+ }
3469
+ };
2773
3470
  const getHttpResponse = (statusCode, body) => {
2774
3471
  let statusText = "";
2775
3472
  if (statusCode === 200) {
@@ -2790,6 +3487,8 @@ export {
2790
3487
  applyFromDriverTransform,
2791
3488
  applySchemaMigrations,
2792
3489
  clearCacheSchedulerTrigger,
3490
+ clusterStatementsSummary,
3491
+ clusterStatementsSummaryHistory,
2793
3492
  ForgeSQLORM as default,
2794
3493
  dropSchemaMigrations,
2795
3494
  dropTableSchemaMigrations,
@@ -2812,6 +3511,8 @@ export {
2812
3511
  migrations,
2813
3512
  nextVal,
2814
3513
  parseDateTime,
2815
- patchDbWithSelectAliased
3514
+ patchDbWithSelectAliased,
3515
+ slowQuery,
3516
+ topSlowestStatementLastHourTrigger
2816
3517
  };
2817
3518
  //# sourceMappingURL=ForgeSQLORM.mjs.map