@constructive-io/graphql-server 4.10.0 → 4.11.1
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 +28 -0
- package/diagnostics/debug-db-snapshot.d.ts +19 -0
- package/diagnostics/debug-db-snapshot.js +196 -0
- package/diagnostics/debug-memory-snapshot.d.ts +53 -0
- package/diagnostics/debug-memory-snapshot.js +77 -0
- package/diagnostics/debug-sampler.d.ts +5 -0
- package/diagnostics/debug-sampler.js +195 -0
- package/diagnostics/observability.d.ts +6 -0
- package/diagnostics/observability.js +62 -0
- package/esm/diagnostics/debug-db-snapshot.js +191 -0
- package/esm/diagnostics/debug-memory-snapshot.js +70 -0
- package/esm/diagnostics/debug-sampler.js +188 -0
- package/esm/diagnostics/observability.js +53 -0
- package/esm/middleware/graphile.js +8 -1
- package/esm/middleware/observability/debug-db.js +23 -0
- package/esm/middleware/observability/debug-memory.js +8 -0
- package/esm/middleware/observability/graphile-build-stats.js +165 -0
- package/esm/middleware/observability/guard.js +14 -0
- package/esm/middleware/observability/request-logger.js +42 -0
- package/esm/server.js +37 -25
- package/middleware/graphile.js +8 -1
- package/middleware/observability/debug-db.d.ts +3 -0
- package/middleware/observability/debug-db.js +27 -0
- package/middleware/observability/debug-memory.d.ts +2 -0
- package/middleware/observability/debug-memory.js +12 -0
- package/middleware/observability/graphile-build-stats.d.ts +45 -0
- package/middleware/observability/graphile-build-stats.js +171 -0
- package/middleware/observability/guard.d.ts +2 -0
- package/middleware/observability/guard.js +18 -0
- package/middleware/observability/request-logger.d.ts +6 -0
- package/middleware/observability/request-logger.js +46 -0
- package/package.json +35 -33
- package/server.d.ts +1 -0
- package/server.js +37 -25
- package/esm/middleware/debug-memory.js +0 -54
- package/middleware/debug-memory.d.ts +0 -15
- package/middleware/debug-memory.js +0 -58
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRequestLogger = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const logger_1 = require("@pgpmjs/logger");
|
|
6
|
+
const log = new logger_1.Logger('server');
|
|
7
|
+
const SAFE_REQUEST_ID = /^[a-zA-Z0-9\-_]{1,128}$/;
|
|
8
|
+
const createRequestLogger = ({ observabilityEnabled }) => {
|
|
9
|
+
return (req, res, next) => {
|
|
10
|
+
const headerRequestId = req.header('x-request-id');
|
|
11
|
+
const reqId = (headerRequestId && SAFE_REQUEST_ID.test(headerRequestId))
|
|
12
|
+
? headerRequestId
|
|
13
|
+
: (0, crypto_1.randomUUID)();
|
|
14
|
+
const start = process.hrtime.bigint();
|
|
15
|
+
let finished = false;
|
|
16
|
+
req.requestId = reqId;
|
|
17
|
+
const host = req.hostname || req.headers.host || 'unknown';
|
|
18
|
+
const ip = req.clientIp ?? req.ip ?? 'unknown';
|
|
19
|
+
log.debug(`[${reqId}] -> ${req.method} ${req.originalUrl} host=${host} ip=${ip}`);
|
|
20
|
+
res.on('finish', () => {
|
|
21
|
+
finished = true;
|
|
22
|
+
const durationMs = Number(process.hrtime.bigint() - start) / 1e6;
|
|
23
|
+
const apiInfo = req.api
|
|
24
|
+
? `db=${req.api.dbname} schemas=${req.api.schema?.join(',') || 'none'}`
|
|
25
|
+
: 'api=unresolved';
|
|
26
|
+
const authInfo = req.token ? 'auth=token' : 'auth=anon';
|
|
27
|
+
const svcInfo = req.svc_key ? `svc=${req.svc_key}` : 'svc=unset';
|
|
28
|
+
log.debug(`[${reqId}] <- ${res.statusCode} ${req.method} ${req.originalUrl} (${durationMs.toFixed(1)} ms) ${apiInfo} ${svcInfo} ${authInfo}`);
|
|
29
|
+
});
|
|
30
|
+
if (observabilityEnabled) {
|
|
31
|
+
res.on('close', () => {
|
|
32
|
+
if (finished) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const durationMs = Number(process.hrtime.bigint() - start) / 1e6;
|
|
36
|
+
const apiInfo = req.api
|
|
37
|
+
? `db=${req.api.dbname} schemas=${req.api.schema?.join(',') || 'none'}`
|
|
38
|
+
: 'api=unresolved';
|
|
39
|
+
log.warn(`[${reqId}] connection closed before response completed ` +
|
|
40
|
+
`${req.method} ${req.originalUrl} (${durationMs.toFixed(1)} ms) ${apiInfo}`);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
next();
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
exports.createRequestLogger = createRequestLogger;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructive-io/graphql-server",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.11.1",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "Constructive GraphQL Server",
|
|
6
6
|
"main": "index.js",
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
"build:dev": "makage build --dev",
|
|
27
27
|
"dev": "ts-node src/run.ts",
|
|
28
28
|
"dev:watch": "nodemon --watch src --ext ts --exec ts-node src/run.ts",
|
|
29
|
+
"debug:memory:analyze": "node scripts/analyze-debug-logs.mjs",
|
|
30
|
+
"debug:heap:capture": "node scripts/capture-heap-snapshot.mjs",
|
|
29
31
|
"lint": "eslint . --fix",
|
|
30
32
|
"test": "jest --passWithNoTests",
|
|
31
33
|
"test:watch": "jest --watch",
|
|
@@ -39,55 +41,55 @@
|
|
|
39
41
|
"backend"
|
|
40
42
|
],
|
|
41
43
|
"dependencies": {
|
|
42
|
-
"@constructive-io/graphql-env": "^3.4.
|
|
43
|
-
"@constructive-io/graphql-types": "^3.3.
|
|
44
|
-
"@constructive-io/s3-utils": "^2.9.
|
|
45
|
-
"@constructive-io/upload-names": "^2.9.
|
|
44
|
+
"@constructive-io/graphql-env": "^3.4.4",
|
|
45
|
+
"@constructive-io/graphql-types": "^3.3.4",
|
|
46
|
+
"@constructive-io/s3-utils": "^2.9.3",
|
|
47
|
+
"@constructive-io/upload-names": "^2.9.3",
|
|
46
48
|
"@constructive-io/url-domains": "^2.9.2",
|
|
47
|
-
"@graphile-contrib/pg-many-to-many": "2.0.0-rc.
|
|
48
|
-
"@graphile/simplify-inflection": "8.0.0-rc.
|
|
49
|
-
"@pgpmjs/env": "^2.15.
|
|
50
|
-
"@pgpmjs/logger": "^2.4.
|
|
51
|
-
"@pgpmjs/server-utils": "^3.4.
|
|
52
|
-
"@pgpmjs/types": "^2.19.
|
|
49
|
+
"@graphile-contrib/pg-many-to-many": "2.0.0-rc.2",
|
|
50
|
+
"@graphile/simplify-inflection": "8.0.0-rc.5",
|
|
51
|
+
"@pgpmjs/env": "^2.15.3",
|
|
52
|
+
"@pgpmjs/logger": "^2.4.3",
|
|
53
|
+
"@pgpmjs/server-utils": "^3.4.4",
|
|
54
|
+
"@pgpmjs/types": "^2.19.3",
|
|
53
55
|
"@pgsql/quotes": "^17.1.0",
|
|
54
56
|
"cors": "^2.8.6",
|
|
55
57
|
"deepmerge": "^4.3.1",
|
|
56
58
|
"express": "^5.2.1",
|
|
57
|
-
"gql-ast": "^3.3.
|
|
58
|
-
"grafast": "1.0.0-rc.
|
|
59
|
-
"grafserv": "1.0.0-rc.
|
|
60
|
-
"graphile-build": "5.0.0-rc.
|
|
61
|
-
"graphile-build-pg": "5.0.0-rc.
|
|
62
|
-
"graphile-cache": "^3.3.
|
|
63
|
-
"graphile-config": "1.0.0-rc.
|
|
64
|
-
"graphile-settings": "^4.9.
|
|
65
|
-
"graphile-utils": "5.0.0-rc.
|
|
66
|
-
"graphql": "
|
|
59
|
+
"gql-ast": "^3.3.3",
|
|
60
|
+
"grafast": "1.0.0-rc.9",
|
|
61
|
+
"grafserv": "1.0.0-rc.7",
|
|
62
|
+
"graphile-build": "5.0.0-rc.6",
|
|
63
|
+
"graphile-build-pg": "5.0.0-rc.8",
|
|
64
|
+
"graphile-cache": "^3.3.4",
|
|
65
|
+
"graphile-config": "1.0.0-rc.6",
|
|
66
|
+
"graphile-settings": "^4.9.2",
|
|
67
|
+
"graphile-utils": "5.0.0-rc.8",
|
|
68
|
+
"graphql": "16.13.0",
|
|
67
69
|
"graphql-upload": "^13.0.0",
|
|
68
|
-
"lru-cache": "^11.2.
|
|
69
|
-
"multer": "^2.1.
|
|
70
|
-
"pg": "^8.
|
|
71
|
-
"pg-cache": "^3.3.
|
|
72
|
-
"pg-env": "^1.7.
|
|
73
|
-
"pg-query-context": "^2.8.
|
|
74
|
-
"pg-sql2": "5.0.0-rc.
|
|
75
|
-
"postgraphile": "5.0.0-rc.
|
|
70
|
+
"lru-cache": "^11.2.7",
|
|
71
|
+
"multer": "^2.1.1",
|
|
72
|
+
"pg": "^8.20.0",
|
|
73
|
+
"pg-cache": "^3.3.4",
|
|
74
|
+
"pg-env": "^1.7.3",
|
|
75
|
+
"pg-query-context": "^2.8.3",
|
|
76
|
+
"pg-sql2": "5.0.0-rc.5",
|
|
77
|
+
"postgraphile": "5.0.0-rc.10",
|
|
76
78
|
"postgraphile-plugin-connection-filter": "3.0.0-rc.1",
|
|
77
79
|
"request-ip": "^3.3.0"
|
|
78
80
|
},
|
|
79
81
|
"devDependencies": {
|
|
80
|
-
"@aws-sdk/client-s3": "^3.
|
|
82
|
+
"@aws-sdk/client-s3": "^3.1009.0",
|
|
81
83
|
"@types/cors": "^2.8.17",
|
|
82
84
|
"@types/express": "^5.0.6",
|
|
83
85
|
"@types/graphql-upload": "^8.0.12",
|
|
84
|
-
"@types/multer": "^2.
|
|
86
|
+
"@types/multer": "^2.1.0",
|
|
85
87
|
"@types/pg": "^8.18.0",
|
|
86
88
|
"@types/request-ip": "^0.0.41",
|
|
87
|
-
"graphile-test": "4.5.
|
|
89
|
+
"graphile-test": "4.5.4",
|
|
88
90
|
"makage": "^0.1.10",
|
|
89
91
|
"nodemon": "^3.1.14",
|
|
90
92
|
"ts-node": "^10.9.2"
|
|
91
93
|
},
|
|
92
|
-
"gitHead": "
|
|
94
|
+
"gitHead": "8afe6b19da82facbe5f3365762ba52888af5b3c9"
|
|
93
95
|
}
|
package/server.d.ts
CHANGED
package/server.js
CHANGED
|
@@ -8,12 +8,13 @@ const graphql_env_1 = require("@constructive-io/graphql-env");
|
|
|
8
8
|
const logger_1 = require("@pgpmjs/logger");
|
|
9
9
|
const server_utils_1 = require("@pgpmjs/server-utils");
|
|
10
10
|
const url_domains_1 = require("@constructive-io/url-domains");
|
|
11
|
-
const crypto_1 = require("crypto");
|
|
12
11
|
const express_1 = __importDefault(require("express"));
|
|
13
12
|
const graphql_upload_1 = __importDefault(require("graphql-upload"));
|
|
14
13
|
const graphile_cache_1 = require("graphile-cache");
|
|
15
14
|
const pg_cache_1 = require("pg-cache");
|
|
16
15
|
const request_ip_1 = __importDefault(require("request-ip"));
|
|
16
|
+
const debug_db_snapshot_1 = require("./diagnostics/debug-db-snapshot");
|
|
17
|
+
const observability_1 = require("./diagnostics/observability");
|
|
17
18
|
const api_1 = require("./middleware/api");
|
|
18
19
|
const auth_1 = require("./middleware/auth");
|
|
19
20
|
const cors_1 = require("./middleware/cors");
|
|
@@ -22,8 +23,12 @@ const favicon_1 = require("./middleware/favicon");
|
|
|
22
23
|
const flush_1 = require("./middleware/flush");
|
|
23
24
|
const graphile_1 = require("./middleware/graphile");
|
|
24
25
|
const multipart_bridge_1 = require("./middleware/multipart-bridge");
|
|
26
|
+
const debug_db_1 = require("./middleware/observability/debug-db");
|
|
27
|
+
const debug_memory_1 = require("./middleware/observability/debug-memory");
|
|
28
|
+
const guard_1 = require("./middleware/observability/guard");
|
|
29
|
+
const request_logger_1 = require("./middleware/observability/request-logger");
|
|
25
30
|
const upload_1 = require("./middleware/upload");
|
|
26
|
-
const
|
|
31
|
+
const debug_sampler_1 = require("./diagnostics/debug-sampler");
|
|
27
32
|
const log = new logger_1.Logger('server');
|
|
28
33
|
/**
|
|
29
34
|
* Creates and starts a GraphQL server instance
|
|
@@ -61,35 +66,17 @@ class Server {
|
|
|
61
66
|
shuttingDown = false;
|
|
62
67
|
closed = false;
|
|
63
68
|
httpServer = null;
|
|
69
|
+
debugSampler = null;
|
|
64
70
|
constructor(opts) {
|
|
65
71
|
this.opts = (0, graphql_env_1.getEnvOptions)(opts);
|
|
66
72
|
const effectiveOpts = this.opts;
|
|
73
|
+
const observabilityRequested = (0, observability_1.isGraphqlObservabilityRequested)();
|
|
74
|
+
const observabilityEnabled = (0, observability_1.isGraphqlObservabilityEnabled)(effectiveOpts.server?.host);
|
|
67
75
|
const app = (0, express_1.default)();
|
|
68
76
|
const api = (0, api_1.createApiMiddleware)(effectiveOpts);
|
|
69
77
|
const authenticate = (0, auth_1.createAuthenticateMiddleware)(effectiveOpts);
|
|
70
78
|
const uploadAuthenticate = (0, upload_1.createUploadAuthenticateMiddleware)(effectiveOpts);
|
|
71
|
-
const
|
|
72
|
-
const requestLogger = (req, res, next) => {
|
|
73
|
-
const headerRequestId = req.header('x-request-id');
|
|
74
|
-
const reqId = (headerRequestId && SAFE_REQUEST_ID.test(headerRequestId))
|
|
75
|
-
? headerRequestId
|
|
76
|
-
: (0, crypto_1.randomUUID)();
|
|
77
|
-
const start = process.hrtime.bigint();
|
|
78
|
-
req.requestId = reqId;
|
|
79
|
-
const host = req.hostname || req.headers.host || 'unknown';
|
|
80
|
-
const ip = req.clientIp || req.ip;
|
|
81
|
-
log.debug(`[${reqId}] -> ${req.method} ${req.originalUrl} host=${host} ip=${ip}`);
|
|
82
|
-
res.on('finish', () => {
|
|
83
|
-
const durationMs = Number(process.hrtime.bigint() - start) / 1e6;
|
|
84
|
-
const apiInfo = req.api
|
|
85
|
-
? `db=${req.api.dbname} schemas=${req.api.schema?.join(',') || 'none'}`
|
|
86
|
-
: 'api=unresolved';
|
|
87
|
-
const authInfo = req.token ? 'auth=token' : 'auth=anon';
|
|
88
|
-
const svcInfo = req.svc_key ? `svc=${req.svc_key}` : 'svc=unset';
|
|
89
|
-
log.debug(`[${reqId}] <- ${res.statusCode} ${req.method} ${req.originalUrl} (${durationMs.toFixed(1)} ms) ${apiInfo} ${svcInfo} ${authInfo}`);
|
|
90
|
-
});
|
|
91
|
-
next();
|
|
92
|
-
};
|
|
79
|
+
const requestLogger = (0, request_logger_1.createRequestLogger)({ observabilityEnabled });
|
|
93
80
|
// Log startup configuration (non-sensitive values only)
|
|
94
81
|
const apiOpts = effectiveOpts.api || {};
|
|
95
82
|
log.info('[server] Starting with config:', {
|
|
@@ -104,9 +91,28 @@ class Server {
|
|
|
104
91
|
exposedSchemas: apiOpts.exposedSchemas?.join(',') || 'none',
|
|
105
92
|
anonRole: apiOpts.anonRole,
|
|
106
93
|
roleName: apiOpts.roleName,
|
|
94
|
+
observabilityEnabled,
|
|
107
95
|
});
|
|
96
|
+
if (observabilityRequested && !observabilityEnabled) {
|
|
97
|
+
const reasons = [];
|
|
98
|
+
if (!(0, observability_1.isDevelopmentObservabilityMode)()) {
|
|
99
|
+
reasons.push('NODE_ENV must be development');
|
|
100
|
+
}
|
|
101
|
+
if (!(0, observability_1.isLoopbackHost)(effectiveOpts.server?.host)) {
|
|
102
|
+
reasons.push('server host must be localhost, 127.0.0.1, or ::1');
|
|
103
|
+
}
|
|
104
|
+
log.warn(`GRAPHQL_OBSERVABILITY_ENABLED was requested but observability remains disabled${reasons.length > 0 ? `: ${reasons.join('; ')}` : ''}`);
|
|
105
|
+
}
|
|
108
106
|
(0, server_utils_1.healthz)(app);
|
|
109
|
-
|
|
107
|
+
if (observabilityEnabled) {
|
|
108
|
+
app.get('/debug/memory', guard_1.localObservabilityOnly, debug_memory_1.debugMemory);
|
|
109
|
+
app.get('/debug/db', guard_1.localObservabilityOnly, (0, debug_db_1.createDebugDatabaseMiddleware)(effectiveOpts));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
app.use('/debug', (_req, res) => {
|
|
113
|
+
res.status(404).send('Not found');
|
|
114
|
+
});
|
|
115
|
+
}
|
|
110
116
|
app.use(favicon_1.favicon);
|
|
111
117
|
(0, server_utils_1.trustProxy)(app, effectiveOpts.server.trustProxy);
|
|
112
118
|
// Warn if a global CORS override is set in production
|
|
@@ -139,6 +145,7 @@ class Server {
|
|
|
139
145
|
app.use(error_handler_1.notFoundHandler); // Catches unmatched routes (404)
|
|
140
146
|
app.use(error_handler_1.errorHandler); // Catches all thrown errors
|
|
141
147
|
this.app = app;
|
|
148
|
+
this.debugSampler = observabilityEnabled ? (0, debug_sampler_1.startDebugSampler)(effectiveOpts) : null;
|
|
142
149
|
}
|
|
143
150
|
listen() {
|
|
144
151
|
const { server } = this.opts;
|
|
@@ -228,9 +235,14 @@ class Server {
|
|
|
228
235
|
this.closed = true;
|
|
229
236
|
this.shuttingDown = true;
|
|
230
237
|
await this.removeEventListener();
|
|
238
|
+
if (this.debugSampler) {
|
|
239
|
+
await this.debugSampler.stop();
|
|
240
|
+
this.debugSampler = null;
|
|
241
|
+
}
|
|
231
242
|
if (this.httpServer?.listening) {
|
|
232
243
|
await new Promise((resolve) => this.httpServer.close(() => resolve()));
|
|
233
244
|
}
|
|
245
|
+
await (0, debug_db_snapshot_1.closeDebugDatabasePools)();
|
|
234
246
|
if (closeCaches) {
|
|
235
247
|
await Server.closeCaches({ closePools: true });
|
|
236
248
|
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { getNodeEnv } from '@pgpmjs/env';
|
|
2
|
-
import { Logger } from '@pgpmjs/logger';
|
|
3
|
-
import { svcCache } from '@pgpmjs/server-utils';
|
|
4
|
-
import { getCacheStats } from 'graphile-cache';
|
|
5
|
-
import { getInFlightCount, getInFlightKeys } from './graphile';
|
|
6
|
-
const log = new Logger('debug-memory');
|
|
7
|
-
const toMB = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
8
|
-
/**
|
|
9
|
-
* Development-only debug endpoint for monitoring memory usage and cache state.
|
|
10
|
-
*
|
|
11
|
-
* Mounts GET /debug/memory which returns:
|
|
12
|
-
* - Node.js process memory (heap, RSS, external, array buffers)
|
|
13
|
-
* - Graphile cache stats (size, max, TTL, keys with ages)
|
|
14
|
-
* - Service cache size
|
|
15
|
-
* - In-flight handler creation count
|
|
16
|
-
* - Process uptime
|
|
17
|
-
*
|
|
18
|
-
* This endpoint is only available when NODE_ENV=development.
|
|
19
|
-
* In production, it returns 404.
|
|
20
|
-
*/
|
|
21
|
-
export const debugMemory = (_req, res) => {
|
|
22
|
-
if (getNodeEnv() !== 'development') {
|
|
23
|
-
res.status(404).send('Not found');
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
const mem = process.memoryUsage();
|
|
27
|
-
const cacheStats = getCacheStats();
|
|
28
|
-
const response = {
|
|
29
|
-
memory: {
|
|
30
|
-
heapUsed: toMB(mem.heapUsed),
|
|
31
|
-
heapTotal: toMB(mem.heapTotal),
|
|
32
|
-
rss: toMB(mem.rss),
|
|
33
|
-
external: toMB(mem.external),
|
|
34
|
-
arrayBuffers: toMB(mem.arrayBuffers),
|
|
35
|
-
},
|
|
36
|
-
graphileCache: {
|
|
37
|
-
size: cacheStats.size,
|
|
38
|
-
max: cacheStats.max,
|
|
39
|
-
ttl: `${(cacheStats.ttl / 1000 / 60).toFixed(0)} min`,
|
|
40
|
-
keys: cacheStats.keys,
|
|
41
|
-
},
|
|
42
|
-
svcCache: {
|
|
43
|
-
size: svcCache.size,
|
|
44
|
-
},
|
|
45
|
-
inFlight: {
|
|
46
|
-
count: getInFlightCount(),
|
|
47
|
-
keys: getInFlightKeys(),
|
|
48
|
-
},
|
|
49
|
-
uptime: `${(process.uptime() / 60).toFixed(1)} min`,
|
|
50
|
-
timestamp: new Date().toISOString(),
|
|
51
|
-
};
|
|
52
|
-
log.debug('Memory snapshot:', response);
|
|
53
|
-
res.json(response);
|
|
54
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { RequestHandler } from 'express';
|
|
2
|
-
/**
|
|
3
|
-
* Development-only debug endpoint for monitoring memory usage and cache state.
|
|
4
|
-
*
|
|
5
|
-
* Mounts GET /debug/memory which returns:
|
|
6
|
-
* - Node.js process memory (heap, RSS, external, array buffers)
|
|
7
|
-
* - Graphile cache stats (size, max, TTL, keys with ages)
|
|
8
|
-
* - Service cache size
|
|
9
|
-
* - In-flight handler creation count
|
|
10
|
-
* - Process uptime
|
|
11
|
-
*
|
|
12
|
-
* This endpoint is only available when NODE_ENV=development.
|
|
13
|
-
* In production, it returns 404.
|
|
14
|
-
*/
|
|
15
|
-
export declare const debugMemory: RequestHandler;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.debugMemory = void 0;
|
|
4
|
-
const env_1 = require("@pgpmjs/env");
|
|
5
|
-
const logger_1 = require("@pgpmjs/logger");
|
|
6
|
-
const server_utils_1 = require("@pgpmjs/server-utils");
|
|
7
|
-
const graphile_cache_1 = require("graphile-cache");
|
|
8
|
-
const graphile_1 = require("./graphile");
|
|
9
|
-
const log = new logger_1.Logger('debug-memory');
|
|
10
|
-
const toMB = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
11
|
-
/**
|
|
12
|
-
* Development-only debug endpoint for monitoring memory usage and cache state.
|
|
13
|
-
*
|
|
14
|
-
* Mounts GET /debug/memory which returns:
|
|
15
|
-
* - Node.js process memory (heap, RSS, external, array buffers)
|
|
16
|
-
* - Graphile cache stats (size, max, TTL, keys with ages)
|
|
17
|
-
* - Service cache size
|
|
18
|
-
* - In-flight handler creation count
|
|
19
|
-
* - Process uptime
|
|
20
|
-
*
|
|
21
|
-
* This endpoint is only available when NODE_ENV=development.
|
|
22
|
-
* In production, it returns 404.
|
|
23
|
-
*/
|
|
24
|
-
const debugMemory = (_req, res) => {
|
|
25
|
-
if ((0, env_1.getNodeEnv)() !== 'development') {
|
|
26
|
-
res.status(404).send('Not found');
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const mem = process.memoryUsage();
|
|
30
|
-
const cacheStats = (0, graphile_cache_1.getCacheStats)();
|
|
31
|
-
const response = {
|
|
32
|
-
memory: {
|
|
33
|
-
heapUsed: toMB(mem.heapUsed),
|
|
34
|
-
heapTotal: toMB(mem.heapTotal),
|
|
35
|
-
rss: toMB(mem.rss),
|
|
36
|
-
external: toMB(mem.external),
|
|
37
|
-
arrayBuffers: toMB(mem.arrayBuffers),
|
|
38
|
-
},
|
|
39
|
-
graphileCache: {
|
|
40
|
-
size: cacheStats.size,
|
|
41
|
-
max: cacheStats.max,
|
|
42
|
-
ttl: `${(cacheStats.ttl / 1000 / 60).toFixed(0)} min`,
|
|
43
|
-
keys: cacheStats.keys,
|
|
44
|
-
},
|
|
45
|
-
svcCache: {
|
|
46
|
-
size: server_utils_1.svcCache.size,
|
|
47
|
-
},
|
|
48
|
-
inFlight: {
|
|
49
|
-
count: (0, graphile_1.getInFlightCount)(),
|
|
50
|
-
keys: (0, graphile_1.getInFlightKeys)(),
|
|
51
|
-
},
|
|
52
|
-
uptime: `${(process.uptime() / 60).toFixed(1)} min`,
|
|
53
|
-
timestamp: new Date().toISOString(),
|
|
54
|
-
};
|
|
55
|
-
log.debug('Memory snapshot:', response);
|
|
56
|
-
res.json(response);
|
|
57
|
-
};
|
|
58
|
-
exports.debugMemory = debugMemory;
|