@onlineapps/service-wrapper 2.1.111 → 2.1.113
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 +6 -6
- package/src/ServiceWrapper.js +36 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/service-wrapper",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.113",
|
|
4
4
|
"description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,16 +25,16 @@
|
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@onlineapps/conn-base-cache": "1.0.8",
|
|
28
|
-
"@onlineapps/conn-base-monitoring": "1.0.
|
|
29
|
-
"@onlineapps/conn-infra-error-handler": "1.0.
|
|
28
|
+
"@onlineapps/conn-base-monitoring": "1.0.10",
|
|
29
|
+
"@onlineapps/conn-infra-error-handler": "1.0.9",
|
|
30
30
|
"@onlineapps/conn-infra-mq": "1.1.67",
|
|
31
31
|
"@onlineapps/conn-orch-api-mapper": "1.0.30",
|
|
32
32
|
"@onlineapps/conn-orch-cookbook": "2.0.35",
|
|
33
|
-
"@onlineapps/conn-orch-orchestrator": "1.0.
|
|
33
|
+
"@onlineapps/conn-orch-orchestrator": "1.0.103",
|
|
34
34
|
"@onlineapps/conn-orch-registry": "1.1.52",
|
|
35
35
|
"@onlineapps/conn-orch-validator": "2.0.29",
|
|
36
|
-
"@onlineapps/monitoring-core": "1.0.
|
|
37
|
-
"@onlineapps/service-common": "1.0.
|
|
36
|
+
"@onlineapps/monitoring-core": "1.0.21",
|
|
37
|
+
"@onlineapps/service-common": "1.0.17",
|
|
38
38
|
"@onlineapps/runtime-config": "1.0.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
package/src/ServiceWrapper.js
CHANGED
|
@@ -973,18 +973,37 @@ class ServiceWrapper {
|
|
|
973
973
|
throw new Error(`Failed to register service ${serviceName}: ${error.message}`);
|
|
974
974
|
}
|
|
975
975
|
|
|
976
|
-
// Start heartbeat
|
|
976
|
+
// Start heartbeat with fail-fast on persistent MQ failure
|
|
977
977
|
const heartbeatInterval = this.config.wrapper?.registry?.heartbeatInterval;
|
|
978
978
|
if (typeof heartbeatInterval !== 'number' || Number.isNaN(heartbeatInterval) || heartbeatInterval <= 0) {
|
|
979
979
|
throw new Error(`[ServiceWrapper] Invalid configuration - wrapper.registry.heartbeatInterval must be a positive number, got: ${heartbeatInterval}`);
|
|
980
980
|
}
|
|
981
981
|
this.logger?.info(`[ServiceWrapper] Heartbeat interval set to ${heartbeatInterval}ms`);
|
|
982
982
|
|
|
983
|
+
const MAX_CONSECUTIVE_FAILURES = this.config.wrapper?.registry?.maxHeartbeatFailures || 5;
|
|
984
|
+
let consecutiveFailures = 0;
|
|
985
|
+
|
|
983
986
|
this.heartbeatTimer = setInterval(async () => {
|
|
984
987
|
try {
|
|
985
988
|
await this.registryClient.sendHeartbeat(serviceName);
|
|
989
|
+
consecutiveFailures = 0;
|
|
986
990
|
} catch (error) {
|
|
987
|
-
|
|
991
|
+
consecutiveFailures++;
|
|
992
|
+
this.logger?.error('Heartbeat failed', {
|
|
993
|
+
error: error.message,
|
|
994
|
+
consecutiveFailures,
|
|
995
|
+
maxBeforeExit: MAX_CONSECUTIVE_FAILURES,
|
|
996
|
+
stack: error.stack
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
1000
|
+
this.logger?.error(
|
|
1001
|
+
`[ServiceWrapper] FATAL: ${consecutiveFailures} consecutive heartbeat failures. ` +
|
|
1002
|
+
`MQ channel is dead and not recovering. Exiting for Docker restart.`,
|
|
1003
|
+
{ consecutiveFailures, serviceName }
|
|
1004
|
+
);
|
|
1005
|
+
process.exit(1);
|
|
1006
|
+
}
|
|
988
1007
|
}
|
|
989
1008
|
}, heartbeatInterval);
|
|
990
1009
|
}
|
|
@@ -1059,20 +1078,32 @@ class ServiceWrapper {
|
|
|
1059
1078
|
_setupHealthChecks() {
|
|
1060
1079
|
const healthEndpoint = this.config.wrapper?.health?.endpoint || '/health';
|
|
1061
1080
|
|
|
1062
|
-
this.app.get(healthEndpoint, (req, res) => {
|
|
1081
|
+
this.app.get(healthEndpoint, async (req, res) => {
|
|
1063
1082
|
const health = {
|
|
1064
1083
|
status: 'healthy',
|
|
1065
1084
|
service: this.config.service?.name || 'unnamed-service',
|
|
1066
1085
|
timestamp: new Date().toISOString(),
|
|
1067
1086
|
components: {
|
|
1068
1087
|
http: 'healthy',
|
|
1069
|
-
mq:
|
|
1088
|
+
mq: 'disabled',
|
|
1070
1089
|
registry: this.registryClient ? 'healthy' : 'disabled',
|
|
1071
1090
|
cache: this.cacheConnector ? 'healthy' : 'disabled'
|
|
1072
1091
|
}
|
|
1073
1092
|
};
|
|
1074
1093
|
|
|
1075
|
-
|
|
1094
|
+
if (this.mqClient) {
|
|
1095
|
+
try {
|
|
1096
|
+
const mqHealth = await this.mqClient.performHealthCheck();
|
|
1097
|
+
health.components.mq = mqHealth.healthy ? 'healthy' : 'unhealthy';
|
|
1098
|
+
if (!mqHealth.healthy) {
|
|
1099
|
+
health.mqIssues = mqHealth.issues;
|
|
1100
|
+
}
|
|
1101
|
+
} catch (e) {
|
|
1102
|
+
health.components.mq = 'unhealthy';
|
|
1103
|
+
health.mqIssues = [e.message];
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1076
1107
|
const statuses = Object.values(health.components);
|
|
1077
1108
|
if (statuses.includes('unhealthy')) {
|
|
1078
1109
|
health.status = 'unhealthy';
|