dt-common-device 13.10.4 → 13.10.6
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/dist/audit/IAuditProperties.d.ts +7 -1
- package/dist/audit/IAuditProperties.js +1 -0
- package/dist/audit/PushAudit.d.ts +12 -1
- package/dist/audit/PushAudit.js +27 -0
- package/dist/queue/entities/HybridHttpQueue.js +2 -2
- package/dist/queue/utils/queueUtils.d.ts +1 -1
- package/dist/queue/utils/queueUtils.js +15 -11
- package/package.json +1 -1
|
@@ -20,7 +20,8 @@ export declare enum Resource {
|
|
|
20
20
|
ACCESS_BLE = "accessBle",
|
|
21
21
|
EMERGENCY_CODE = "emergencyCode",
|
|
22
22
|
EMERGENCY_RFID = "emergencyRfid",
|
|
23
|
-
EMERGENCY_BLE = "emergencyBle"
|
|
23
|
+
EMERGENCY_BLE = "emergencyBle",
|
|
24
|
+
SERVER = "server"
|
|
24
25
|
}
|
|
25
26
|
export interface IAuditProperties {
|
|
26
27
|
resource: Resource;
|
|
@@ -45,3 +46,8 @@ export interface IAuditProperties {
|
|
|
45
46
|
eventData?: any;
|
|
46
47
|
[key: string]: any;
|
|
47
48
|
}
|
|
49
|
+
export type PartialAuditProperties = Omit<IAuditProperties, "resource" | "source" | "propertyId"> & {
|
|
50
|
+
resource?: Resource;
|
|
51
|
+
source?: Source;
|
|
52
|
+
propertyId?: string;
|
|
53
|
+
};
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import { IAuditProperties } from "./IAuditProperties";
|
|
1
|
+
import { IAuditProperties, PartialAuditProperties } from "./IAuditProperties";
|
|
2
2
|
/**
|
|
3
3
|
* Publishes an audit event. Failures are logged and swallowed so callers can
|
|
4
4
|
* fire-and-forget without causing unhandled promise rejections when the audit
|
|
5
5
|
* pipeline returns 5xx or times out (see e.g. Sentry DT-PMS-C).
|
|
6
|
+
* USE FOR PROPERTY LEVEL AUDITS
|
|
6
7
|
*/
|
|
7
8
|
export declare function pushAudit(data: {
|
|
8
9
|
auditType: string;
|
|
9
10
|
auditData: IAuditProperties;
|
|
10
11
|
}): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Publishes an audit event. Failures are logged and swallowed so callers can
|
|
14
|
+
* fire-and-forget without causing unhandled promise rejections when the audit
|
|
15
|
+
* pipeline returns 5xx or times out (see e.g. Sentry DT-PMS-C).
|
|
16
|
+
* USE FOR SYSTEM LEVEL AUDITS (If Property ID is not available)
|
|
17
|
+
*/
|
|
18
|
+
export declare function pushSystemAudit(data: {
|
|
19
|
+
auditType: string;
|
|
20
|
+
auditData: PartialAuditProperties;
|
|
21
|
+
}): Promise<void>;
|
package/dist/audit/PushAudit.js
CHANGED
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.pushAudit = pushAudit;
|
|
7
|
+
exports.pushSystemAudit = pushSystemAudit;
|
|
7
8
|
const dt_audit_library_1 = require("dt-audit-library");
|
|
8
9
|
const config_1 = require("../config/config");
|
|
9
10
|
const AuditUtils_1 = require("./AuditUtils");
|
|
@@ -12,6 +13,7 @@ const typedi_1 = __importDefault(require("typedi"));
|
|
|
12
13
|
* Publishes an audit event. Failures are logged and swallowed so callers can
|
|
13
14
|
* fire-and-forget without causing unhandled promise rejections when the audit
|
|
14
15
|
* pipeline returns 5xx or times out (see e.g. Sentry DT-PMS-C).
|
|
16
|
+
* USE FOR PROPERTY LEVEL AUDITS
|
|
15
17
|
*/
|
|
16
18
|
async function pushAudit(data) {
|
|
17
19
|
try {
|
|
@@ -33,3 +35,28 @@ async function pushAudit(data) {
|
|
|
33
35
|
: { auditType: data.auditType });
|
|
34
36
|
}
|
|
35
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Publishes an audit event. Failures are logged and swallowed so callers can
|
|
40
|
+
* fire-and-forget without causing unhandled promise rejections when the audit
|
|
41
|
+
* pipeline returns 5xx or times out (see e.g. Sentry DT-PMS-C).
|
|
42
|
+
* USE FOR SYSTEM LEVEL AUDITS (If Property ID is not available)
|
|
43
|
+
*/
|
|
44
|
+
async function pushSystemAudit(data) {
|
|
45
|
+
try {
|
|
46
|
+
await (0, dt_audit_library_1.publishAudit)({
|
|
47
|
+
eventType: data.auditType,
|
|
48
|
+
properties: {
|
|
49
|
+
...data.auditData,
|
|
50
|
+
timestamp: new Date().toISOString(),
|
|
51
|
+
env_type: process.env.NODE_ENV,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
const status = err?.response?.status;
|
|
57
|
+
const code = err?.code;
|
|
58
|
+
(0, config_1.getLogger)().error(`pushSystemAudit failed for ${data.auditType}: ${err?.message ?? err}`, status != null || code != null
|
|
59
|
+
? { auditType: data.auditType, status, code }
|
|
60
|
+
: { auditType: data.auditType });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -99,7 +99,7 @@ let HybridHttpQueue = (() => {
|
|
|
99
99
|
(0, config_1.getConfig)().LOGGER.info(`Request queued: ${method} ${url} -> ${provider} [${connectionId}]. Job ID: ${jobId}, Delay: ${delay}ms`);
|
|
100
100
|
// Wait for job completion and return result
|
|
101
101
|
// Simple: delay + windowMs + HTTP buffer timeout
|
|
102
|
-
return queueUtils_1.QueueUtils.waitForJobCompletion(jobId, queueKey, delay, windowMs);
|
|
102
|
+
return queueUtils_1.QueueUtils.waitForJobCompletion(jobId, queueKey, delay, windowMs, this.queues);
|
|
103
103
|
}
|
|
104
104
|
async processHttpRequest(job) {
|
|
105
105
|
// Log immediately when worker picks up the job
|
|
@@ -147,7 +147,7 @@ let HybridHttpQueue = (() => {
|
|
|
147
147
|
return this.handleRequest(url, method, httpCallOption);
|
|
148
148
|
}
|
|
149
149
|
async handleRequest(url, method, options) {
|
|
150
|
-
const { connectionId, provider
|
|
150
|
+
const { connectionId, provider } = jobUtils_1.JobUtils.extractConnectionDetails(options);
|
|
151
151
|
// Check rate limit first
|
|
152
152
|
const allowed = await rateLimit_utils_1.RateLimitUtils.isRateLimitAllowed(connectionId, provider, this.rateLimitConfigs);
|
|
153
153
|
if (allowed) {
|
|
@@ -8,5 +8,5 @@ export declare class QueueUtils {
|
|
|
8
8
|
static waitForRateLimitExpiry(connectionId: string, provider: string, rateLimitConfigs: Map<string, IRateLimitConfig>): Promise<void>;
|
|
9
9
|
static executeHttpRequest(url: string, method: string, options: HttpCallOption, connectionId: string, provider: string): Promise<any>;
|
|
10
10
|
static addJobToQueue(queueKey: string, jobData: any, delay: number, queues: Map<string, any>): Promise<string>;
|
|
11
|
-
static waitForJobCompletion(jobId: string, queueKey: string, jobDelay
|
|
11
|
+
static waitForJobCompletion(jobId: string, queueKey: string, jobDelay: number | undefined, windowMs: number | undefined, queues: Map<string, any>): Promise<any>;
|
|
12
12
|
}
|
|
@@ -35,6 +35,8 @@ class QueueUtils {
|
|
|
35
35
|
console.log(`[${new Date().toISOString()}] [getOrCreateWorker] Existing worker running status: ${isRunning}`);
|
|
36
36
|
if (!isRunning) {
|
|
37
37
|
console.log(`[${new Date().toISOString()}] [getOrCreateWorker] Worker not running, closing and recreating...`);
|
|
38
|
+
// Stale worker detected — surface at WARN level with count context
|
|
39
|
+
(0, config_1.getConfig)().LOGGER.warn(`Stale worker for queue ${queueKey} (not running), recreating. Active workers before recreate: ${workers.size}`);
|
|
38
40
|
try {
|
|
39
41
|
existingWorker.close();
|
|
40
42
|
}
|
|
@@ -116,7 +118,8 @@ class QueueUtils {
|
|
|
116
118
|
});
|
|
117
119
|
workers.set(queueKey, worker);
|
|
118
120
|
console.log(`[${new Date().toISOString()}] [getOrCreateWorker] Worker initialized and stored for queue: ${queueKey}`);
|
|
119
|
-
|
|
121
|
+
// Log total active workers — a growing count here signals a connection-proliferation leak
|
|
122
|
+
(0, config_1.getConfig)().LOGGER.info(`Worker initialized for queue: ${queueKey} (total active workers: ${workers.size})`);
|
|
120
123
|
}
|
|
121
124
|
static async waitForRateLimitExpiry(connectionId, provider, rateLimitConfigs) {
|
|
122
125
|
const key = `rate_limit:${provider}:${connectionId}`;
|
|
@@ -202,22 +205,25 @@ class QueueUtils {
|
|
|
202
205
|
queue.getActiveCount(),
|
|
203
206
|
]);
|
|
204
207
|
console.log(`[${new Date().toISOString()}] [addJobToQueue] Queue state - QueueKey: ${queueKey}, Waiting: ${waiting}, Delayed: ${delayed}, Active: ${active}`);
|
|
208
|
+
// Warn via structured logger when backlog is building — correlates with heap stats
|
|
209
|
+
if (waiting + delayed > 1) {
|
|
210
|
+
(0, config_1.getConfig)().LOGGER.warn(`Queue backlog [${queueKey}]: waiting=${waiting} delayed=${delayed} active=${active}`);
|
|
211
|
+
}
|
|
205
212
|
}
|
|
206
213
|
catch (e) {
|
|
207
214
|
// Ignore errors getting queue state
|
|
208
215
|
}
|
|
209
216
|
return job.id;
|
|
210
217
|
}
|
|
211
|
-
static async waitForJobCompletion(jobId, queueKey, jobDelay = 0, windowMs = 60000) {
|
|
218
|
+
static async waitForJobCompletion(jobId, queueKey, jobDelay = 0, windowMs = 60000, queues) {
|
|
212
219
|
// Simple: delay (wait) + windowMs (execute) + HTTP timeout buffer
|
|
213
220
|
const httpTimeout = 60000;
|
|
214
221
|
const totalTimeout = jobDelay + windowMs + httpTimeout;
|
|
215
222
|
console.log(`[${new Date().toISOString()}] [waitForJobCompletion] Waiting for job ${jobId} - delay: ${jobDelay}ms, timeout: ${totalTimeout}ms`);
|
|
216
223
|
const startTime = Date.now();
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
});
|
|
224
|
+
// Reuse the existing queue instance — never create a new Queue per call
|
|
225
|
+
// (each new Queue duplicates the Redis connection, causing a connection leak)
|
|
226
|
+
const queue = this.getOrCreateQueue(queueKey, queues);
|
|
221
227
|
return new Promise(async (resolve, reject) => {
|
|
222
228
|
const checkInterval = setInterval(async () => {
|
|
223
229
|
try {
|
|
@@ -226,7 +232,6 @@ class QueueUtils {
|
|
|
226
232
|
const elapsed = Date.now() - startTime;
|
|
227
233
|
if (elapsed > totalTimeout) {
|
|
228
234
|
clearInterval(checkInterval);
|
|
229
|
-
await queue.close();
|
|
230
235
|
return reject(new Error(`Job ${jobId} not found and timeout exceeded`));
|
|
231
236
|
}
|
|
232
237
|
return; // Job not found yet, keep checking
|
|
@@ -236,26 +241,25 @@ class QueueUtils {
|
|
|
236
241
|
console.log(`[${new Date().toISOString()}] [waitForJobCompletion] Job ${jobId} completed`);
|
|
237
242
|
clearInterval(checkInterval);
|
|
238
243
|
const result = job.returnvalue;
|
|
239
|
-
await queue.close();
|
|
240
244
|
return resolve(result);
|
|
241
245
|
}
|
|
242
246
|
if (state === "failed") {
|
|
243
247
|
console.log(`[${new Date().toISOString()}] [waitForJobCompletion] Job ${jobId} failed: ${job.failedReason}`);
|
|
244
248
|
clearInterval(checkInterval);
|
|
245
|
-
await queue.close();
|
|
246
249
|
return reject(new Error(job.failedReason || "Job failed"));
|
|
247
250
|
}
|
|
248
251
|
}
|
|
249
252
|
catch (error) {
|
|
250
253
|
clearInterval(checkInterval);
|
|
251
|
-
await queue.close();
|
|
252
254
|
reject(error);
|
|
253
255
|
}
|
|
254
256
|
}, 500); // Check every 500ms
|
|
255
257
|
// Timeout
|
|
256
258
|
setTimeout(() => {
|
|
257
259
|
clearInterval(checkInterval);
|
|
258
|
-
|
|
260
|
+
// Log via structured logger before rejecting so the timeout is visible in
|
|
261
|
+
// CloudWatch alongside the periodic heap stats — correlates queue stalls with memory pressure.
|
|
262
|
+
(0, config_1.getConfig)().LOGGER.error(`Job ${jobId} timed out after ${totalTimeout}ms [${queueKey}] (jobDelay=${jobDelay}ms)`);
|
|
259
263
|
reject(new Error(`Request timeout: Maximum wait time exceeded (${totalTimeout}ms). Job delay was ${jobDelay}ms.`));
|
|
260
264
|
}, totalTimeout);
|
|
261
265
|
});
|