@onlineapps/service-wrapper 2.0.49 → 2.0.51
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/package.json +1 -1
- package/src/ServiceWrapper.js +126 -47
package/package.json
CHANGED
package/src/ServiceWrapper.js
CHANGED
|
@@ -485,6 +485,11 @@ class ServiceWrapper {
|
|
|
485
485
|
console.warn('Monitoring initialization failed (non-critical):', error.message);
|
|
486
486
|
}
|
|
487
487
|
}
|
|
488
|
+
|
|
489
|
+
// Register MQ channel close hooks and health monitoring AFTER monitoring is initialized
|
|
490
|
+
if (this.mqClient) {
|
|
491
|
+
await this._registerMQHooks();
|
|
492
|
+
}
|
|
488
493
|
|
|
489
494
|
// Initialize cache if configured
|
|
490
495
|
if (this.config.wrapper?.cache?.enabled === true) {
|
|
@@ -557,6 +562,119 @@ class ServiceWrapper {
|
|
|
557
562
|
}
|
|
558
563
|
}
|
|
559
564
|
|
|
565
|
+
/**
|
|
566
|
+
* Run health check before workflow step (debug mode only)
|
|
567
|
+
* @private
|
|
568
|
+
* @param {Object} message - Workflow message
|
|
569
|
+
*/
|
|
570
|
+
async _runHealthCheckBeforeWorkflowStep(message) {
|
|
571
|
+
if (!this.mqClient || !this.mqClient._transport) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const transport = this.mqClient._transport;
|
|
576
|
+
|
|
577
|
+
// Check if transport has performHealthCheck method (public API)
|
|
578
|
+
if (typeof transport.performHealthCheck === 'function') {
|
|
579
|
+
try {
|
|
580
|
+
const health = await transport.performHealthCheck();
|
|
581
|
+
|
|
582
|
+
this.logger?.debug('[ServiceWrapper] [DEBUG] Health check before workflow step', {
|
|
583
|
+
workflow_id: message.workflow_id || message.workflowId,
|
|
584
|
+
current_step: message.current_step,
|
|
585
|
+
health: {
|
|
586
|
+
healthy: health.healthy,
|
|
587
|
+
issues: health.issues,
|
|
588
|
+
consumers: {
|
|
589
|
+
active: health.consumers.active,
|
|
590
|
+
tracked: health.consumers.tracked
|
|
591
|
+
},
|
|
592
|
+
queues: {
|
|
593
|
+
checked: health.queues.checked,
|
|
594
|
+
missing: health.queues.missing,
|
|
595
|
+
noConsumer: health.queues.noConsumer
|
|
596
|
+
},
|
|
597
|
+
channels: health.channels
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
if (!health.healthy) {
|
|
602
|
+
this.logger?.warn('[ServiceWrapper] [DEBUG] Health check FAILED before workflow step', {
|
|
603
|
+
workflow_id: message.workflow_id || message.workflowId,
|
|
604
|
+
issues: health.issues
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
} catch (err) {
|
|
608
|
+
this.logger?.warn('[ServiceWrapper] [DEBUG] Health check error before workflow step', {
|
|
609
|
+
error: err.message,
|
|
610
|
+
workflow_id: message.workflow_id || message.workflowId
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Register MQ channel close hooks and health monitoring
|
|
618
|
+
* Must be called AFTER monitoring is initialized
|
|
619
|
+
* @private
|
|
620
|
+
*/
|
|
621
|
+
async _registerMQHooks() {
|
|
622
|
+
if (!this.mqClient || !this.mqClient._transport) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Access the underlying transport (RabbitMQClient) to register hooks
|
|
627
|
+
const transport = this.mqClient._transport;
|
|
628
|
+
|
|
629
|
+
if (typeof transport.onChannelClose === 'function') {
|
|
630
|
+
// Register hook for all channel types
|
|
631
|
+
transport.onChannelClose('all', (details) => {
|
|
632
|
+
const logData = {
|
|
633
|
+
type: details.type,
|
|
634
|
+
reason: details.reason,
|
|
635
|
+
error: details.error ? {
|
|
636
|
+
message: details.error.message,
|
|
637
|
+
code: details.error.code,
|
|
638
|
+
stack: details.error.stack
|
|
639
|
+
} : null,
|
|
640
|
+
timestamp: details.timestamp,
|
|
641
|
+
connectionState: details.connectionState,
|
|
642
|
+
channelCreatedAt: details.channelCreatedAt,
|
|
643
|
+
lastOperation: details.lastOperation
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
if (this.monitoring && this.monitoring.error) {
|
|
647
|
+
this.monitoring.error(`MQ Channel Closed: ${details.type}`, logData);
|
|
648
|
+
}
|
|
649
|
+
console.error(`[ServiceWrapper] [MQ] Channel closed: ${details.type} - ${details.reason}`);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
this.logger?.info('MQ channel close hooks registered');
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Register health shutdown handler
|
|
656
|
+
if (typeof transport.on === 'function') {
|
|
657
|
+
transport.on('health:shutdown', (health) => {
|
|
658
|
+
console.error('[ServiceWrapper] [MQ] Health shutdown triggered - shutting down service');
|
|
659
|
+
if (this.monitoring && this.monitoring.error) {
|
|
660
|
+
this.monitoring.error('MQ Health Shutdown', {
|
|
661
|
+
issues: health.issues,
|
|
662
|
+
timestamp: health.timestamp
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
// Graceful shutdown
|
|
666
|
+
this.shutdown().then(() => {
|
|
667
|
+
process.exit(1);
|
|
668
|
+
}).catch((err) => {
|
|
669
|
+
console.error('[ServiceWrapper] Shutdown error:', err);
|
|
670
|
+
process.exit(1);
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
this.logger?.info('MQ health shutdown handler registered');
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
560
678
|
/**
|
|
561
679
|
* Initialize MQ connection
|
|
562
680
|
* @private
|
|
@@ -570,9 +688,10 @@ class ServiceWrapper {
|
|
|
570
688
|
const serviceName = this.config.service?.name || 'unnamed-service';
|
|
571
689
|
|
|
572
690
|
// Health monitoring and channel close hooks configuration
|
|
691
|
+
// NOTE: Health monitoring is NOT run periodically - it's triggered manually before workflow steps in debug mode
|
|
573
692
|
const healthCheckConfig = {
|
|
574
|
-
healthCheckInterval: this.config.wrapper?.mq?.healthCheckInterval || 30000, //
|
|
575
|
-
healthCheckEnabled:
|
|
693
|
+
healthCheckInterval: this.config.wrapper?.mq?.healthCheckInterval || 30000, // Not used when healthCheckEnabled=false
|
|
694
|
+
healthCheckEnabled: false, // Disable periodic checks - we'll run manually before workflow steps
|
|
576
695
|
criticalHealthShutdown: this.config.wrapper?.mq?.criticalHealthShutdown !== false, // Default: true
|
|
577
696
|
criticalHealthShutdownDelay: this.config.wrapper?.mq?.criticalHealthShutdownDelay || 60000, // 60s
|
|
578
697
|
// Health reporting callbacks
|
|
@@ -624,51 +743,6 @@ class ServiceWrapper {
|
|
|
624
743
|
await this.mqClient.connect();
|
|
625
744
|
this.logger?.info('MQ connector initialized');
|
|
626
745
|
|
|
627
|
-
// Register channel close hooks after connection is established
|
|
628
|
-
// Access the underlying transport (RabbitMQClient) via _transport
|
|
629
|
-
if (this.mqClient._transport && typeof this.mqClient._transport.onChannelClose === 'function') {
|
|
630
|
-
// Register hook for all channel types
|
|
631
|
-
this.mqClient._transport.onChannelClose('all', (details) => {
|
|
632
|
-
const logData = {
|
|
633
|
-
type: details.type,
|
|
634
|
-
reason: details.reason,
|
|
635
|
-
error: details.error ? {
|
|
636
|
-
message: details.error.message,
|
|
637
|
-
code: details.error.code,
|
|
638
|
-
stack: details.error.stack
|
|
639
|
-
} : null,
|
|
640
|
-
timestamp: details.timestamp,
|
|
641
|
-
connectionState: details.connectionState,
|
|
642
|
-
channelCreatedAt: details.channelCreatedAt,
|
|
643
|
-
lastOperation: details.lastOperation
|
|
644
|
-
};
|
|
645
|
-
|
|
646
|
-
if (this.monitoring && this.monitoring.error) {
|
|
647
|
-
this.monitoring.error(`MQ Channel Closed: ${details.type}`, logData);
|
|
648
|
-
}
|
|
649
|
-
console.error(`[ServiceWrapper] [MQ] Channel closed: ${details.type} - ${details.reason}`);
|
|
650
|
-
});
|
|
651
|
-
|
|
652
|
-
// Register health shutdown handler
|
|
653
|
-
this.mqClient._transport.on('health:shutdown', (health) => {
|
|
654
|
-
console.error('[ServiceWrapper] [MQ] Health shutdown triggered - shutting down service');
|
|
655
|
-
if (this.monitoring && this.monitoring.error) {
|
|
656
|
-
this.monitoring.error('MQ Health Shutdown', {
|
|
657
|
-
issues: health.issues,
|
|
658
|
-
timestamp: health.timestamp
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
// Graceful shutdown
|
|
662
|
-
this.shutdown().then(() => {
|
|
663
|
-
process.exit(1);
|
|
664
|
-
}).catch((err) => {
|
|
665
|
-
console.error('[ServiceWrapper] Shutdown error:', err);
|
|
666
|
-
process.exit(1);
|
|
667
|
-
});
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
this.logger?.info('MQ channel close hooks and health monitoring registered');
|
|
671
|
-
}
|
|
672
746
|
} catch (error) {
|
|
673
747
|
const message = `[MQConnector] Unable to connect to RabbitMQ at ${mqUrl}. ` +
|
|
674
748
|
'Verify that api_services_queuer is running and accessible. ' +
|
|
@@ -1175,6 +1249,11 @@ class ServiceWrapper {
|
|
|
1175
1249
|
throw new Error('Workflow message must have current_step field or cookbook with at least one step');
|
|
1176
1250
|
}
|
|
1177
1251
|
|
|
1252
|
+
// DEBUG MODE: Run health check before workflow step
|
|
1253
|
+
if (process.env.DEBUG === 'true' || process.env.NODE_ENV === 'development') {
|
|
1254
|
+
await this._runHealthCheckBeforeWorkflowStep(message);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1178
1257
|
// Normalize message format: Gateway sends workflowId, orchestrator expects workflow_id
|
|
1179
1258
|
const normalizedMessage = {
|
|
1180
1259
|
...message,
|