@resolveio/server-lib 20.12.0 → 20.12.2

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.
@@ -7,9 +7,6 @@ export interface SlowQueryReportPayload {
7
7
  durationMs: number;
8
8
  maxDurationMs?: number;
9
9
  thresholdMs?: number;
10
- explainPlan?: Record<string, any>;
11
- explainExecutionStats?: Record<string, any>;
12
- explainGeneratedAt?: Date;
13
10
  clientSlug?: string;
14
11
  clientName?: string;
15
12
  environment?: string;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/models/slow-query-report.model.ts"],"names":[],"mappings":"","file":"slow-query-report.model.js","sourcesContent":["export interface SlowQueryReportPayload {\n\tqueryHash: string;\n\tcollection: string;\n\tfilter?: Record<string, any>;\n\tpipeline?: any[];\n\toptions?: Record<string, any>;\n\tdurationMs: number;\n\tmaxDurationMs?: number;\n\tthresholdMs?: number;\n\texplainPlan?: Record<string, any>;\n\texplainExecutionStats?: Record<string, any>;\n\texplainGeneratedAt?: Date;\n\tclientSlug?: string;\n\tclientName?: string;\n\tenvironment?: string;\n\tsourceApp?: string;\n\tnodeHostname?: string;\n\tstatus?: string;\n\tignored?: boolean;\n\tnotes?: string;\n\tapiKey?: string;\n}\n"]}
1
+ {"version":3,"sources":["../../src/models/slow-query-report.model.ts"],"names":[],"mappings":"","file":"slow-query-report.model.js","sourcesContent":["export interface SlowQueryReportPayload {\n\tqueryHash: string;\n\tcollection: string;\n\tfilter?: Record<string, any>;\n\tpipeline?: any[];\n\toptions?: Record<string, any>;\n\tdurationMs: number;\n\tmaxDurationMs?: number;\n\tthresholdMs?: number;\n\tclientSlug?: string;\n\tclientName?: string;\n\tenvironment?: string;\n\tsourceApp?: string;\n\tnodeHostname?: string;\n\tstatus?: string;\n\tignored?: boolean;\n\tnotes?: string;\n\tapiKey?: string;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resolveio/server-lib",
3
- "version": "20.12.0",
3
+ "version": "20.12.2",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "package": "./build_package.sh",
@@ -11,14 +11,11 @@ interface SlowQueryReportParams {
11
11
  sourceApp?: string;
12
12
  }
13
13
  export declare class SlowQueryReporter {
14
- private static _explainCache;
15
14
  static reportSlowQuery(params: SlowQueryReportParams): Promise<void>;
16
15
  static reportSlowQueryFireAndForget(params: SlowQueryReportParams): void;
17
16
  private static getThresholdMs;
18
17
  private static getEnvironment;
19
18
  private static getClientName;
20
- private static captureExplain;
21
- private static shouldExplain;
22
19
  private static sendPayload;
23
20
  private static getApiKey;
24
21
  private static generateQueryHash;
@@ -38,19 +38,17 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.SlowQueryReporter = void 0;
40
40
  var crypto_1 = require("crypto");
41
- var NodeCache = require("node-cache");
42
41
  var os_1 = require("os");
43
42
  var resolveio_server_app_1 = require("../resolveio-server-app");
44
43
  var common_1 = require("./common");
45
44
  var SLOW_QUERY_ENDPOINT = 'https://backend.resolveio.com/api/slow-queries/report';
46
45
  var DEFAULT_SLOW_QUERY_THRESHOLD_MS = 2000;
47
- var EXPLAIN_THROTTLE_SECONDS = 5 * 60;
48
46
  var SlowQueryReporter = /** @class */ (function () {
49
47
  function SlowQueryReporter() {
50
48
  }
51
49
  SlowQueryReporter.reportSlowQuery = function (params) {
52
50
  return __awaiter(this, void 0, void 0, function () {
53
- var thresholdMs, queryHash, apiKey, payload, explain, err_1;
51
+ var thresholdMs, queryHash, apiKey, payload, err_1;
54
52
  return __generator(this, function (_a) {
55
53
  switch (_a.label) {
56
54
  case 0:
@@ -78,26 +76,16 @@ var SlowQueryReporter = /** @class */ (function () {
78
76
  };
79
77
  _a.label = 1;
80
78
  case 1:
81
- _a.trys.push([1, 4, , 5]);
82
- return [4 /*yield*/, this.captureExplain(params, queryHash)];
83
- case 2:
84
- explain = _a.sent();
85
- if (explain) {
86
- payload.explainPlan = (0, common_1.deepCopy)(explain.queryPlanner || explain);
87
- if (explain.executionStats) {
88
- payload.explainExecutionStats = (0, common_1.deepCopy)(explain.executionStats);
89
- }
90
- payload.explainGeneratedAt = new Date();
91
- }
79
+ _a.trys.push([1, 3, , 4]);
92
80
  return [4 /*yield*/, this.sendPayload(payload, apiKey)];
93
- case 3:
81
+ case 2:
94
82
  _a.sent();
95
- return [3 /*break*/, 5];
96
- case 4:
83
+ return [3 /*break*/, 4];
84
+ case 3:
97
85
  err_1 = _a.sent();
98
86
  console.error('SlowQueryReporter failed', err_1);
99
- return [3 /*break*/, 5];
100
- case 5: return [2 /*return*/];
87
+ return [3 /*break*/, 4];
88
+ case 4: return [2 /*return*/];
101
89
  }
102
90
  });
103
91
  });
@@ -136,56 +124,9 @@ var SlowQueryReporter = /** @class */ (function () {
136
124
  var config = resolveio_server_app_1.ResolveIOServer.getServerConfig();
137
125
  return (config === null || config === void 0 ? void 0 : config.CLIENT_NAME) || resolveio_server_app_1.ResolveIOServer.getClientName();
138
126
  };
139
- SlowQueryReporter.captureExplain = function (params, queryHash) {
140
- return __awaiter(this, void 0, void 0, function () {
141
- var collection, err_2;
142
- return __generator(this, function (_a) {
143
- switch (_a.label) {
144
- case 0:
145
- if (params.queryType === 'countDocuments') {
146
- return [2 /*return*/, undefined];
147
- }
148
- if (!this.shouldExplain(queryHash)) {
149
- return [2 /*return*/, undefined];
150
- }
151
- collection = resolveio_server_app_1.ResolveIOServer.getMainDB().collection(params.collection);
152
- _a.label = 1;
153
- case 1:
154
- _a.trys.push([1, 8, , 9]);
155
- if (!(params.queryType === 'aggregate')) return [3 /*break*/, 3];
156
- return [4 /*yield*/, collection.aggregate(params.pipeline || [], params.options).explain('executionStats')];
157
- case 2: return [2 /*return*/, _a.sent()];
158
- case 3:
159
- if (!(params.queryType === 'find')) return [3 /*break*/, 5];
160
- return [4 /*yield*/, collection.find(params.filter || {}, params.options).explain('executionStats')];
161
- case 4: return [2 /*return*/, _a.sent()];
162
- case 5:
163
- if (!(params.queryType === 'findOne')) return [3 /*break*/, 7];
164
- return [4 /*yield*/, collection.find(params.filter || {}, params.options).limit(1).explain('executionStats')];
165
- case 6: return [2 /*return*/, _a.sent()];
166
- case 7: return [3 /*break*/, 9];
167
- case 8:
168
- err_2 = _a.sent();
169
- console.error('SlowQueryReporter explain failed', err_2);
170
- return [3 /*break*/, 9];
171
- case 9: return [2 /*return*/, undefined];
172
- }
173
- });
174
- });
175
- };
176
- SlowQueryReporter.shouldExplain = function (queryHash) {
177
- if (!queryHash) {
178
- return false;
179
- }
180
- if (this._explainCache.has(queryHash)) {
181
- return false;
182
- }
183
- this._explainCache.set(queryHash, true);
184
- return true;
185
- };
186
127
  SlowQueryReporter.sendPayload = function (payload, apiKey) {
187
128
  return __awaiter(this, void 0, void 0, function () {
188
- var transport, key, headers, response, err_3;
129
+ var transport, key, headers, response, err_2;
189
130
  return __generator(this, function (_a) {
190
131
  switch (_a.label) {
191
132
  case 0:
@@ -220,8 +161,8 @@ var SlowQueryReporter = /** @class */ (function () {
220
161
  }
221
162
  return [3 /*break*/, 4];
222
163
  case 3:
223
- err_3 = _a.sent();
224
- console.error('SlowQueryReporter network error', err_3);
164
+ err_2 = _a.sent();
165
+ console.error('SlowQueryReporter network error', err_2);
225
166
  return [3 /*break*/, 4];
226
167
  case 4: return [2 /*return*/];
227
168
  }
@@ -276,7 +217,6 @@ var SlowQueryReporter = /** @class */ (function () {
276
217
  }
277
218
  return value;
278
219
  };
279
- SlowQueryReporter._explainCache = new NodeCache({ stdTTL: EXPLAIN_THROTTLE_SECONDS, checkperiod: 0 });
280
220
  return SlowQueryReporter;
281
221
  }());
282
222
  exports.SlowQueryReporter = SlowQueryReporter;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/slow-query-reporter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iCAAoC;AAEpC,sCAAwC;AACxC,yBAA8B;AAE9B,gEAA0D;AAC1D,mCAAoC;AAEpC,IAAM,mBAAmB,GAAG,uDAAuD,CAAC;AACpF,IAAM,+BAA+B,GAAG,IAAI,CAAC;AAC7C,IAAM,wBAAwB,GAAG,CAAC,GAAG,EAAE,CAAC;AAcxC;IAAA;IA4NA,CAAC;IAzNoB,iCAAe,GAAnC,UAAoC,MAA6B;;;;;;wBAC1D,WAAW,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;wBAC1C,IAAI,WAAW,IAAI,CAAC,IAAI,MAAM,CAAC,UAAU,GAAG,WAAW,EAAE,CAAC;4BACzD,sBAAO;wBACR,CAAC;wBAEK,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;wBAC/D,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;wBAC1B,OAAO,GAA2B;4BACvC,SAAS,WAAA;4BACT,UAAU,EAAE,MAAM,CAAC,UAAU;4BAC7B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAA,iBAAQ,EAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;4BACjE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAA,iBAAQ,EAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;4BAC3D,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAA,iBAAQ,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;4BAC9D,UAAU,EAAE,MAAM,CAAC,UAAU;4BAC7B,WAAW,aAAA;4BACX,UAAU,EAAE,sCAAe,CAAC,aAAa,EAAE;4BAC3C,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE;4BAChC,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,eAAe;4BAC9C,WAAW,EAAE,IAAI,CAAC,cAAc,EAAE;4BAClC,YAAY,EAAE,IAAA,aAAQ,GAAE;4BACxB,KAAK,EAAE,MAAM,CAAC,KAAK;4BACnB,MAAM,QAAA;yBACN,CAAC;;;;wBAGe,qBAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,EAAA;;wBAAtD,OAAO,GAAG,SAA4C;wBAC5D,IAAI,OAAO,EAAE,CAAC;4BACb,OAAO,CAAC,WAAW,GAAG,IAAA,iBAAQ,EAAC,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,CAAC;4BAChE,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;gCAC5B,OAAO,CAAC,qBAAqB,GAAG,IAAA,iBAAQ,EAAC,OAAO,CAAC,cAAc,CAAC,CAAC;4BAClE,CAAC;4BACD,OAAO,CAAC,kBAAkB,GAAG,IAAI,IAAI,EAAE,CAAC;wBACzC,CAAC;wBAED,qBAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,EAAA;;wBAAvC,SAAuC,CAAC;;;;wBAGxC,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAG,CAAC,CAAC;;;;;;KAEhD;IAEa,8CAA4B,GAA1C,UAA2C,MAA6B;QACvE,8EAA8E;QAC9E,KAAK,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,UAAA,GAAG;YAC1C,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,GAAG,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;IACJ,CAAC;IAEc,gCAAc,GAA7B;QACC,IAAM,MAAM,GAAG,sCAAe,CAAC,eAAe,EAAE,CAAC;QACjD,IAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;QACrD,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;YACjD,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACtC,CAAC;QAED,IAAM,WAAW,GAAG,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,uBAAuB,CAAC;QACpD,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;YACrC,OAAO,WAAW,CAAC;QACpB,CAAC;QAED,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;YACjG,OAAO,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,+BAA+B,CAAC;IACxC,CAAC;IAEc,gCAAc,GAA7B;QACC,IAAM,MAAM,GAAG,sCAAe,CAAC,eAAe,EAAE,CAAC;QACjD,OAAO,CACN,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,aAAa;aACrB,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,WAAW,CAAA;aACnB,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,QAAQ,CAAA;YAChB,OAAO,CAAC,GAAG,CAAC,aAAa;YACzB,OAAO,CAAC,GAAG,CAAC,QAAQ;YACpB,SAAS,CACT,CAAC;IACH,CAAC;IAEc,+BAAa,GAA5B;QACC,IAAM,MAAM,GAAG,sCAAe,CAAC,eAAe,EAAE,CAAC;QACjD,OAAO,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,WAAW,KAAI,sCAAe,CAAC,aAAa,EAAE,CAAC;IAC/D,CAAC;IAEoB,gCAAc,GAAnC,UAAoC,MAA6B,EAAE,SAAiB;;;;;;wBACnF,IAAI,MAAM,CAAC,SAAS,KAAK,gBAAgB,EAAE,CAAC;4BAC3C,sBAAO,SAAS,EAAC;wBAClB,CAAC;wBAED,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;4BACpC,sBAAO,SAAS,EAAC;wBAClB,CAAC;wBAEK,UAAU,GAAG,sCAAe,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;;;;6BAExE,CAAA,MAAM,CAAC,SAAS,KAAK,WAAW,CAAA,EAAhC,wBAAgC;wBAC5B,qBAAM,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAA;4BAAlG,sBAAO,SAA2F,EAAC;;6BAE3F,CAAA,MAAM,CAAC,SAAS,KAAK,MAAM,CAAA,EAA3B,wBAA2B;wBAC5B,qBAAM,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAA;4BAA3F,sBAAO,SAAoF,EAAC;;6BAEpF,CAAA,MAAM,CAAC,SAAS,KAAK,SAAS,CAAA,EAA9B,wBAA8B;wBAC/B,qBAAM,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAA;4BAApG,sBAAO,SAA6F,EAAC;;;;wBAItG,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,KAAG,CAAC,CAAC;;4BAGxD,sBAAO,SAAS,EAAC;;;;KACjB;IAEc,+BAAa,GAA5B,UAA6B,SAAiB;QAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACvC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC;IACb,CAAC;IAEoB,6BAAW,GAAhC,UAAiC,OAA+B,EAAE,MAAe;;;;;;wBAC1E,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC;wBACnC,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;4BACrC,OAAO,CAAC,KAAK,CAAC,uDAAuD,EAAE,OAAO,CAAC,CAAC;4BAChF,sBAAO;wBACR,CAAC;wBAEK,GAAG,GAAG,MAAM,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;wBACjC,OAAO,GAAG,iBAAiB,CAAC;4BACjC,cAAc,EAAE,kBAAkB;4BAClC,4BAA4B,EAAE,GAAG;4BACjC,kBAAkB,EAAE,GAAG;4BACvB,WAAW,EAAE,GAAG;yBAChB,CAAC,CAAC;;;;wBAGe,qBAAM,SAAS,CAAC,mBAAmB,EAAE;gCACrD,MAAM,EAAE,MAAM;gCACd,OAAO,SAAA;gCACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;6BAC7B,CAAC,EAAA;;wBAJI,QAAQ,GAAG,SAIf;wBAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;4BAClB,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE;gCACnD,MAAM,EAAE,QAAQ,CAAC,MAAM;gCACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;gCAC/B,SAAS,EAAE,OAAO,CAAC,SAAS;6BAC5B,CAAC,CAAC;wBACJ,CAAC;;;;wBAGD,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAG,CAAC,CAAC;;;;;;KAEvD;IAEc,2BAAS,GAAxB;QACC,IAAM,MAAM,GAAG,sCAAe,CAAC,eAAe,EAAE,CAAC;QACjD,OAAO,CACN,OAAO,CAAC,GAAG,CAAC,kBAAkB;YAC9B,OAAO,CAAC,GAAG,CAAC,qBAAqB;aACjC,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,kBAAkB,CAAA;aAC1B,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,qBAAqB,CAAA;YAC7B,sCAAe,CAAC,aAAa,EAAE,CAC/B,CAAC;IACH,CAAC;IAEc,mCAAiB,GAAhC,UAAiC,MAA6B;QAC7D,IAAI,CAAC;YACJ,IAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC;gBACxC,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,IAAI;gBAC7B,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI;gBACjC,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI;gBAC/B,SAAS,EAAE,MAAM,CAAC,SAAS;aAC3B,CAAC,CAAC;YACH,IAAM,MAAM,GAAG,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAClG,OAAO,UAAG,MAAM,CAAC,UAAU,cAAI,MAAM,CAAE,CAAC;QACzC,CAAC;QACD,OAAO,GAAG,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,GAAG,CAAC,CAAC;YAC/D,OAAO,UAAG,MAAM,CAAC,UAAU,cAAI,IAAI,CAAC,GAAG,EAAE,CAAE,CAAC;QAC7C,CAAC;IACF,CAAC;IAEc,kCAAgB,GAA/B,UAAgC,KAAU;QAA1C,iBA0BC;QAzBA,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC3C,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,GAAG,CAAC,UAAA,IAAI,IAAI,OAAA,KAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAA3B,CAA2B,CAAC,CAAC;QACvD,CAAC;QAED,IAAI,KAAK,YAAY,MAAM,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC;QAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/B,IAAI,OAAQ,KAAa,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;gBACtD,OAAQ,KAAa,CAAC,WAAW,EAAE,CAAC;YACrC,CAAC;YAED,IAAM,SAAO,GAAwB,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,UAAA,GAAG;gBACpC,SAAO,CAAC,GAAG,CAAC,GAAG,KAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YAClD,CAAC,CAAC,CAAC;YACH,OAAO,SAAO,CAAC;QAChB,CAAC;QAED,OAAO,KAAK,CAAC;IACd,CAAC;IA1Nc,+BAAa,GAAG,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,wBAAwB,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;IA2NpG,wBAAC;CA5ND,AA4NC,IAAA;AA5NY,8CAAiB;AA8N7B,SAAS,iBAAiB,CAAgC,GAAM;IAC/D,OAAQ,MAAM,CAAC,IAAI,CAAC,GAAG,CAAoB,CAAC,MAAM,CAAC,UAAC,GAAG,EAAE,GAAG;QAC3D,IAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAC3C,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAClB,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC,EAAE,EAAO,CAAC,CAAC;AACb,CAAC","file":"slow-query-reporter.js","sourcesContent":["import { createHash } from 'crypto';\nimport { Document } from 'mongodb';\nimport * as NodeCache from 'node-cache';\nimport { hostname } from 'os';\nimport { SlowQueryReportPayload } from '../models/slow-query-report.model';\nimport { ResolveIOServer } from '../resolveio-server-app';\nimport { deepCopy } from './common';\n\nconst SLOW_QUERY_ENDPOINT = 'https://backend.resolveio.com/api/slow-queries/report';\nconst DEFAULT_SLOW_QUERY_THRESHOLD_MS = 2000;\nconst EXPLAIN_THROTTLE_SECONDS = 5 * 60;\n\ninterface SlowQueryReportParams {\n\tcollection: string;\n\tfilter?: Document;\n\tpipeline?: any[];\n\toptions?: Document;\n\tdurationMs: number;\n\tqueryType: 'find' | 'findOne' | 'aggregate' | 'countDocuments';\n\tnotes?: string;\n\tqueryHash?: string;\n\tsourceApp?: string;\n}\n\nexport class SlowQueryReporter {\n\tprivate static _explainCache = new NodeCache({ stdTTL: EXPLAIN_THROTTLE_SECONDS, checkperiod: 0 });\n\n\tpublic static async reportSlowQuery(params: SlowQueryReportParams): Promise<void> {\n\t\tconst thresholdMs = this.getThresholdMs();\n\t\tif (thresholdMs <= 0 || params.durationMs < thresholdMs) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst queryHash = params.queryHash || this.generateQueryHash(params);\n\t\tconst apiKey = this.getApiKey();\n\t\tconst payload: SlowQueryReportPayload = {\n\t\t\tqueryHash,\n\t\t\tcollection: params.collection,\n\t\t\tpipeline: params.pipeline ? deepCopy(params.pipeline) : undefined,\n\t\t\tfilter: params.filter ? deepCopy(params.filter) : undefined,\n\t\t\toptions: params.options ? deepCopy(params.options) : undefined,\n\t\t\tdurationMs: params.durationMs,\n\t\t\tthresholdMs,\n\t\t\tclientSlug: ResolveIOServer.getClientName(),\n\t\t\tclientName: this.getClientName(),\n\t\t\tsourceApp: params.sourceApp || 'mongo-manager',\n\t\t\tenvironment: this.getEnvironment(),\n\t\t\tnodeHostname: hostname(),\n\t\t\tnotes: params.notes,\n\t\t\tapiKey\n\t\t};\n\n\t\ttry {\n\t\t\tconst explain = await this.captureExplain(params, queryHash);\n\t\t\tif (explain) {\n\t\t\t\tpayload.explainPlan = deepCopy(explain.queryPlanner || explain);\n\t\t\t\tif (explain.executionStats) {\n\t\t\t\t\tpayload.explainExecutionStats = deepCopy(explain.executionStats);\n\t\t\t\t}\n\t\t\t\tpayload.explainGeneratedAt = new Date();\n\t\t\t}\n\n\t\t\tawait this.sendPayload(payload, apiKey);\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter failed', err);\n\t\t}\n\t}\n\n\tpublic static reportSlowQueryFireAndForget(params: SlowQueryReportParams): void {\n\t\t// eslint-disable-next-line no-restricted-syntax, promise/prefer-await-to-then\n\t\tvoid this.reportSlowQuery(params).catch(err => {\n\t\t\tconsole.error('SlowQueryReporter fire-and-forget error', err);\n\t\t});\n\t}\n\n\tprivate static getThresholdMs(): number {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\tconst envValue = process.env.SLOW_QUERY_THRESHOLD_MS;\n\t\tif (envValue && !Number.isNaN(Number(envValue))) {\n\t\t\treturn Number.parseInt(envValue, 10);\n\t\t}\n\n\t\tconst configValue = config?.SLOW_QUERY_THRESHOLD_MS;\n\t\tif (typeof configValue === 'number') {\n\t\t\treturn configValue;\n\t\t}\n\n\t\tif (typeof configValue === 'string' && configValue.length && !Number.isNaN(Number(configValue))) {\n\t\t\treturn Number.parseInt(configValue, 10);\n\t\t}\n\n\t\treturn DEFAULT_SLOW_QUERY_THRESHOLD_MS;\n\t}\n\n\tprivate static getEnvironment(): string {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn (\n\t\t\tconfig?.RESOLVEIO_ENV ||\n\t\t\tconfig?.ENVIRONMENT ||\n\t\t\tconfig?.ROOT_URL ||\n\t\t\tprocess.env.RESOLVEIO_ENV ||\n\t\t\tprocess.env.NODE_ENV ||\n\t\t\t'unknown'\n\t\t);\n\t}\n\n\tprivate static getClientName(): string | undefined {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn config?.CLIENT_NAME || ResolveIOServer.getClientName();\n\t}\n\n\tprivate static async captureExplain(params: SlowQueryReportParams, queryHash: string): Promise<Document | undefined> {\n\t\tif (params.queryType === 'countDocuments') {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (!this.shouldExplain(queryHash)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst collection = ResolveIOServer.getMainDB().collection(params.collection);\n\t\ttry {\n\t\t\tif (params.queryType === 'aggregate') {\n\t\t\t\treturn await collection.aggregate(params.pipeline || [], params.options).explain('executionStats');\n\t\t\t}\n\t\t\telse if (params.queryType === 'find') {\n\t\t\t\treturn await collection.find(params.filter || {}, params.options).explain('executionStats');\n\t\t\t}\n\t\t\telse if (params.queryType === 'findOne') {\n\t\t\t\treturn await collection.find(params.filter || {}, params.options).limit(1).explain('executionStats');\n\t\t\t}\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter explain failed', err);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate static shouldExplain(queryHash: string): boolean {\n\t\tif (!queryHash) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this._explainCache.has(queryHash)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._explainCache.set(queryHash, true);\n\t\treturn true;\n\t}\n\n\tprivate static async sendPayload(payload: SlowQueryReportPayload, apiKey?: string): Promise<void> {\n\t\tconst transport = globalThis.fetch;\n\t\tif (typeof transport !== 'function') {\n\t\t\tconsole.error('SlowQueryReporter cannot send payload (fetch missing)', payload);\n\t\t\treturn;\n\t\t}\n\n\t\tconst key = apiKey || this.getApiKey();\n\t\tconst headers = removeEmptyFields({\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'X-ResolveIO-Slow-Query-Key': key,\n\t\t\t'X-Slow-Query-Key': key,\n\t\t\t'X-API-Key': key\n\t\t});\n\n\t\ttry {\n\t\t\tconst response = await transport(SLOW_QUERY_ENDPOINT, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify(payload)\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconsole.error('SlowQueryReporter payload rejected', {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\tstatusText: response.statusText,\n\t\t\t\t\tqueryHash: payload.queryHash\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter network error', err);\n\t\t}\n\t}\n\n\tprivate static getApiKey(): string | undefined {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn (\n\t\t\tprocess.env.SLOW_QUERY_API_KEY ||\n\t\t\tprocess.env.SLOW_QUERY_INGEST_KEY ||\n\t\t\tconfig?.SLOW_QUERY_API_KEY ||\n\t\t\tconfig?.SLOW_QUERY_INGEST_KEY ||\n\t\t\tResolveIOServer.getClientName()\n\t\t);\n\t}\n\n\tprivate static generateQueryHash(params: SlowQueryReportParams): string {\n\t\ttry {\n\t\t\tconst normalized = this.normalizeForHash({\n\t\t\t\tcollection: params.collection,\n\t\t\t\tfilter: params.filter || null,\n\t\t\t\tpipeline: params.pipeline || null,\n\t\t\t\toptions: params.options || null,\n\t\t\t\tqueryType: params.queryType\n\t\t\t});\n\t\t\tconst digest = createHash('sha256').update(JSON.stringify(normalized)).digest('hex').slice(0, 16);\n\t\t\treturn `${params.collection}-${digest}`;\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter hash generation failed', err);\n\t\t\treturn `${params.collection}-${Date.now()}`;\n\t\t}\n\t}\n\n\tprivate static normalizeForHash(value: any): any {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn value;\n\t\t}\n\n\t\tif (Array.isArray(value)) {\n\t\t\treturn value.map(item => this.normalizeForHash(item));\n\t\t}\n\n\t\tif (value instanceof RegExp) {\n\t\t\treturn value.toString();\n\t\t}\n\n\t\tif (typeof value === 'object') {\n\t\t\tif (typeof (value as any).toHexString === 'function') {\n\t\t\t\treturn (value as any).toHexString();\n\t\t\t}\n\n\t\t\tconst ordered: Record<string, any> = {};\n\t\t\tObject.keys(value).sort().forEach(key => {\n\t\t\t\tordered[key] = this.normalizeForHash(value[key]);\n\t\t\t});\n\t\t\treturn ordered;\n\t\t}\n\n\t\treturn value;\n\t}\n}\n\n\tfunction removeEmptyFields<T extends Record<string, any>>(obj: T): T {\n\t\treturn (Object.keys(obj) as Array<keyof T>).reduce((acc, key) => {\n\t\t\tconst value = obj[key];\n\t\t\tif (value !== undefined && value !== null) {\n\t\t\t\tacc[key] = value;\n\t\t\t}\n\t\t\treturn acc;\n\t\t}, {} as T);\n\t}\n"]}
1
+ {"version":3,"sources":["../../src/util/slow-query-reporter.ts"],"names":[],"mappings":"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","file":"slow-query-reporter.js","sourcesContent":["import { createHash } from 'crypto';\nimport { Document } from 'mongodb';\nimport { hostname } from 'os';\nimport { SlowQueryReportPayload } from '../models/slow-query-report.model';\nimport { ResolveIOServer } from '../resolveio-server-app';\nimport { deepCopy } from './common';\n\nconst SLOW_QUERY_ENDPOINT = 'https://backend.resolveio.com/api/slow-queries/report';\nconst DEFAULT_SLOW_QUERY_THRESHOLD_MS = 2000;\n\ninterface SlowQueryReportParams {\n\tcollection: string;\n\tfilter?: Document;\n\tpipeline?: any[];\n\toptions?: Document;\n\tdurationMs: number;\n\tqueryType: 'find' | 'findOne' | 'aggregate' | 'countDocuments';\n\tnotes?: string;\n\tqueryHash?: string;\n\tsourceApp?: string;\n}\n\nexport class SlowQueryReporter {\n\tpublic static async reportSlowQuery(params: SlowQueryReportParams): Promise<void> {\n\t\tconst thresholdMs = this.getThresholdMs();\n\t\tif (thresholdMs <= 0 || params.durationMs < thresholdMs) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst queryHash = params.queryHash || this.generateQueryHash(params);\n\t\tconst apiKey = this.getApiKey();\n\t\tconst payload: SlowQueryReportPayload = {\n\t\t\tqueryHash,\n\t\t\tcollection: params.collection,\n\t\t\tpipeline: params.pipeline ? deepCopy(params.pipeline) : undefined,\n\t\t\tfilter: params.filter ? deepCopy(params.filter) : undefined,\n\t\t\toptions: params.options ? deepCopy(params.options) : undefined,\n\t\t\tdurationMs: params.durationMs,\n\t\t\tthresholdMs,\n\t\t\tclientSlug: ResolveIOServer.getClientName(),\n\t\t\tclientName: this.getClientName(),\n\t\t\tsourceApp: params.sourceApp || 'mongo-manager',\n\t\t\tenvironment: this.getEnvironment(),\n\t\t\tnodeHostname: hostname(),\n\t\t\tnotes: params.notes,\n\t\t\tapiKey\n\t\t};\n\n\t\ttry {\n\t\t\tawait this.sendPayload(payload, apiKey);\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter failed', err);\n\t\t}\n\t}\n\n\tpublic static reportSlowQueryFireAndForget(params: SlowQueryReportParams): void {\n\t\t// eslint-disable-next-line no-restricted-syntax, promise/prefer-await-to-then\n\t\tvoid this.reportSlowQuery(params).catch(err => {\n\t\t\tconsole.error('SlowQueryReporter fire-and-forget error', err);\n\t\t});\n\t}\n\n\tprivate static getThresholdMs(): number {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\tconst envValue = process.env.SLOW_QUERY_THRESHOLD_MS;\n\t\tif (envValue && !Number.isNaN(Number(envValue))) {\n\t\t\treturn Number.parseInt(envValue, 10);\n\t\t}\n\n\t\tconst configValue = config?.SLOW_QUERY_THRESHOLD_MS;\n\t\tif (typeof configValue === 'number') {\n\t\t\treturn configValue;\n\t\t}\n\n\t\tif (typeof configValue === 'string' && configValue.length && !Number.isNaN(Number(configValue))) {\n\t\t\treturn Number.parseInt(configValue, 10);\n\t\t}\n\n\t\treturn DEFAULT_SLOW_QUERY_THRESHOLD_MS;\n\t}\n\n\tprivate static getEnvironment(): string {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn (\n\t\t\tconfig?.RESOLVEIO_ENV ||\n\t\t\tconfig?.ENVIRONMENT ||\n\t\t\tconfig?.ROOT_URL ||\n\t\t\tprocess.env.RESOLVEIO_ENV ||\n\t\t\tprocess.env.NODE_ENV ||\n\t\t\t'unknown'\n\t\t);\n\t}\n\n\tprivate static getClientName(): string | undefined {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn config?.CLIENT_NAME || ResolveIOServer.getClientName();\n\t}\n\n\tprivate static async sendPayload(payload: SlowQueryReportPayload, apiKey?: string): Promise<void> {\n\t\tconst transport = globalThis.fetch;\n\t\tif (typeof transport !== 'function') {\n\t\t\tconsole.error('SlowQueryReporter cannot send payload (fetch missing)', payload);\n\t\t\treturn;\n\t\t}\n\n\t\tconst key = apiKey || this.getApiKey();\n\t\tconst headers = removeEmptyFields({\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'X-ResolveIO-Slow-Query-Key': key,\n\t\t\t'X-Slow-Query-Key': key,\n\t\t\t'X-API-Key': key\n\t\t});\n\n\t\ttry {\n\t\t\tconst response = await transport(SLOW_QUERY_ENDPOINT, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify(payload)\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconsole.error('SlowQueryReporter payload rejected', {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\tstatusText: response.statusText,\n\t\t\t\t\tqueryHash: payload.queryHash\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter network error', err);\n\t\t}\n\t}\n\n\tprivate static getApiKey(): string | undefined {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn (\n\t\t\tprocess.env.SLOW_QUERY_API_KEY ||\n\t\t\tprocess.env.SLOW_QUERY_INGEST_KEY ||\n\t\t\tconfig?.SLOW_QUERY_API_KEY ||\n\t\t\tconfig?.SLOW_QUERY_INGEST_KEY ||\n\t\t\tResolveIOServer.getClientName()\n\t\t);\n\t}\n\n\tprivate static generateQueryHash(params: SlowQueryReportParams): string {\n\t\ttry {\n\t\t\tconst normalized = this.normalizeForHash({\n\t\t\t\tcollection: params.collection,\n\t\t\t\tfilter: params.filter || null,\n\t\t\t\tpipeline: params.pipeline || null,\n\t\t\t\toptions: params.options || null,\n\t\t\t\tqueryType: params.queryType\n\t\t\t});\n\t\t\tconst digest = createHash('sha256').update(JSON.stringify(normalized)).digest('hex').slice(0, 16);\n\t\t\treturn `${params.collection}-${digest}`;\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter hash generation failed', err);\n\t\t\treturn `${params.collection}-${Date.now()}`;\n\t\t}\n\t}\n\n\tprivate static normalizeForHash(value: any): any {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn value;\n\t\t}\n\n\t\tif (Array.isArray(value)) {\n\t\t\treturn value.map(item => this.normalizeForHash(item));\n\t\t}\n\n\t\tif (value instanceof RegExp) {\n\t\t\treturn value.toString();\n\t\t}\n\n\t\tif (typeof value === 'object') {\n\t\t\tif (typeof (value as any).toHexString === 'function') {\n\t\t\t\treturn (value as any).toHexString();\n\t\t\t}\n\n\t\t\tconst ordered: Record<string, any> = {};\n\t\t\tObject.keys(value).sort().forEach(key => {\n\t\t\t\tordered[key] = this.normalizeForHash(value[key]);\n\t\t\t});\n\t\t\treturn ordered;\n\t\t}\n\n\t\treturn value;\n\t}\n\n}\n\nfunction removeEmptyFields<T extends Record<string, any>>(obj: T): T {\n\treturn (Object.keys(obj) as Array<keyof T>).reduce((acc, key) => {\n\t\tconst value = obj[key];\n\t\tif (value !== undefined && value !== null) {\n\t\t\tacc[key] = value;\n\t\t}\n\t\treturn acc;\n\t}, {} as T);\n}\n"]}