@morojs/moro 1.6.5 → 1.6.8
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 +20 -4
- package/dist/core/auth/morojs-adapter.js +17 -14
- package/dist/core/auth/morojs-adapter.js.map +1 -1
- package/dist/core/config/config-sources.js +44 -0
- package/dist/core/config/config-sources.js.map +1 -1
- package/dist/core/database/adapters/drizzle.js +5 -5
- package/dist/core/database/adapters/drizzle.js.map +1 -1
- package/dist/core/database/adapters/mongodb.js +5 -1
- package/dist/core/database/adapters/mongodb.js.map +1 -1
- package/dist/core/database/adapters/mysql.js +5 -1
- package/dist/core/database/adapters/mysql.js.map +1 -1
- package/dist/core/database/adapters/postgresql.js +1 -1
- package/dist/core/database/adapters/postgresql.js.map +1 -1
- package/dist/core/database/adapters/redis.js +2 -2
- package/dist/core/database/adapters/redis.js.map +1 -1
- package/dist/core/database/adapters/sqlite.js +5 -1
- package/dist/core/database/adapters/sqlite.js.map +1 -1
- package/dist/core/docs/index.js.map +1 -1
- package/dist/core/docs/simple-docs.js +2 -1
- package/dist/core/docs/simple-docs.js.map +1 -1
- package/dist/core/docs/swagger-ui.js +1 -0
- package/dist/core/docs/swagger-ui.js.map +1 -1
- package/dist/core/docs/zod-to-openapi.js +4 -0
- package/dist/core/docs/zod-to-openapi.js.map +1 -1
- package/dist/core/events/event-bus.d.ts +1 -1
- package/dist/core/events/event-bus.js +8 -4
- package/dist/core/events/event-bus.js.map +1 -1
- package/dist/core/framework.d.ts +1 -1
- package/dist/core/framework.js +3 -1
- package/dist/core/framework.js.map +1 -1
- package/dist/core/graphql/adapter.d.ts +73 -0
- package/dist/core/graphql/adapter.js +2 -0
- package/dist/core/graphql/adapter.js.map +1 -0
- package/dist/core/graphql/adapters/graphql-js-adapter.d.ts +26 -0
- package/dist/core/graphql/adapters/graphql-js-adapter.js +229 -0
- package/dist/core/graphql/adapters/graphql-js-adapter.js.map +1 -0
- package/dist/core/graphql/core.d.ts +60 -0
- package/dist/core/graphql/core.js +165 -0
- package/dist/core/graphql/core.js.map +1 -0
- package/dist/core/graphql/index.d.ts +4 -0
- package/dist/core/graphql/index.js +4 -0
- package/dist/core/graphql/index.js.map +1 -0
- package/dist/core/graphql/loader.d.ts +9 -0
- package/dist/core/graphql/loader.js +32 -0
- package/dist/core/graphql/loader.js.map +1 -0
- package/dist/core/graphql/types.d.ts +211 -0
- package/dist/core/graphql/types.js +2 -0
- package/dist/core/graphql/types.js.map +1 -0
- package/dist/core/http/http-server.d.ts +7 -0
- package/dist/core/http/http-server.js +267 -123
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/utils/uws-worker-clustering.d.ts +28 -0
- package/dist/core/http/utils/uws-worker-clustering.js +313 -0
- package/dist/core/http/utils/uws-worker-clustering.js.map +1 -0
- package/dist/core/http/uws-http-server.d.ts +7 -1
- package/dist/core/http/uws-http-server.js +272 -189
- package/dist/core/http/uws-http-server.js.map +1 -1
- package/dist/core/jobs/cron-parser.d.ts +62 -0
- package/dist/core/jobs/cron-parser.js +239 -0
- package/dist/core/jobs/cron-parser.js.map +1 -0
- package/dist/core/jobs/index.d.ts +12 -0
- package/dist/core/jobs/index.js +9 -0
- package/dist/core/jobs/index.js.map +1 -0
- package/dist/core/jobs/job-executor.d.ts +134 -0
- package/dist/core/jobs/job-executor.js +413 -0
- package/dist/core/jobs/job-executor.js.map +1 -0
- package/dist/core/jobs/job-scheduler.d.ts +214 -0
- package/dist/core/jobs/job-scheduler.js +551 -0
- package/dist/core/jobs/job-scheduler.js.map +1 -0
- package/dist/core/jobs/job-state-manager.d.ts +158 -0
- package/dist/core/jobs/job-state-manager.js +444 -0
- package/dist/core/jobs/job-state-manager.js.map +1 -0
- package/dist/core/jobs/leader-election.d.ts +124 -0
- package/dist/core/jobs/leader-election.js +481 -0
- package/dist/core/jobs/leader-election.js.map +1 -0
- package/dist/core/jobs/types.d.ts +151 -0
- package/dist/core/jobs/types.js +4 -0
- package/dist/core/jobs/types.js.map +1 -0
- package/dist/core/jobs/utils.d.ts +95 -0
- package/dist/core/jobs/utils.js +258 -0
- package/dist/core/jobs/utils.js.map +1 -0
- package/dist/core/logger/filters.js +2 -0
- package/dist/core/logger/filters.js.map +1 -1
- package/dist/core/logger/logger.d.ts +7 -5
- package/dist/core/logger/logger.js +68 -27
- package/dist/core/logger/logger.js.map +1 -1
- package/dist/core/logger/outputs.js +2 -0
- package/dist/core/logger/outputs.js.map +1 -1
- package/dist/core/middleware/built-in/auth/helpers.js +1 -1
- package/dist/core/middleware/built-in/auth/helpers.js.map +1 -1
- package/dist/core/middleware/built-in/auth/jwt-helpers.js +1 -1
- package/dist/core/middleware/built-in/auth/jwt-helpers.js.map +1 -1
- package/dist/core/middleware/built-in/auth/providers.js +1 -1
- package/dist/core/middleware/built-in/auth/providers.js.map +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/file.js +3 -3
- package/dist/core/middleware/built-in/cache/adapters/cache/file.js.map +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/memory.js +1 -0
- package/dist/core/middleware/built-in/cache/adapters/cache/memory.js.map +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/redis.js +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/redis.js.map +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.d.ts +8 -0
- package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js +100 -7
- package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js.map +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.d.ts +6 -0
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js +97 -13
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js.map +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js.map +1 -1
- package/dist/core/middleware/built-in/cookie/hook.d.ts +1 -1
- package/dist/core/middleware/built-in/cookie/hook.js +2 -2
- package/dist/core/middleware/built-in/cookie/hook.js.map +1 -1
- package/dist/core/middleware/built-in/csrf/core.js +1 -0
- package/dist/core/middleware/built-in/csrf/core.js.map +1 -1
- package/dist/core/middleware/built-in/graphql/core.d.ts +11 -0
- package/dist/core/middleware/built-in/graphql/core.js +24 -0
- package/dist/core/middleware/built-in/graphql/core.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/helpers.d.ts +69 -0
- package/dist/core/middleware/built-in/graphql/helpers.js +187 -0
- package/dist/core/middleware/built-in/graphql/helpers.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/hook.d.ts +7 -0
- package/dist/core/middleware/built-in/graphql/hook.js +78 -0
- package/dist/core/middleware/built-in/graphql/hook.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/index.d.ts +5 -0
- package/dist/core/middleware/built-in/graphql/index.js +5 -0
- package/dist/core/middleware/built-in/graphql/index.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/middleware.d.ts +7 -0
- package/dist/core/middleware/built-in/graphql/middleware.js +54 -0
- package/dist/core/middleware/built-in/graphql/middleware.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/subscriptions.d.ts +20 -0
- package/dist/core/middleware/built-in/graphql/subscriptions.js +37 -0
- package/dist/core/middleware/built-in/graphql/subscriptions.js.map +1 -0
- package/dist/core/middleware/built-in/index.d.ts +2 -1
- package/dist/core/middleware/built-in/index.js +3 -0
- package/dist/core/middleware/built-in/index.js.map +1 -1
- package/dist/core/middleware/built-in/rate-limit/core.d.ts +5 -0
- package/dist/core/middleware/built-in/rate-limit/core.js +16 -8
- package/dist/core/middleware/built-in/rate-limit/core.js.map +1 -1
- package/dist/core/middleware/built-in/validation/core.js +42 -19
- package/dist/core/middleware/built-in/validation/core.js.map +1 -1
- package/dist/core/middleware/index.js +1 -0
- package/dist/core/middleware/index.js.map +1 -1
- package/dist/core/modules/auto-discovery.js +5 -4
- package/dist/core/modules/auto-discovery.js.map +1 -1
- package/dist/core/modules/modules.js.map +1 -1
- package/dist/core/networking/adapters/socketio-adapter.js +1 -1
- package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
- package/dist/core/networking/adapters/uws-adapter.js +7 -2
- package/dist/core/networking/adapters/uws-adapter.js.map +1 -1
- package/dist/core/networking/adapters/ws-adapter.js +5 -2
- package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
- package/dist/core/networking/websocket-manager.js +2 -0
- package/dist/core/networking/websocket-manager.js.map +1 -1
- package/dist/core/pooling/object-pool-manager.d.ts +8 -2
- package/dist/core/pooling/object-pool-manager.js +38 -18
- package/dist/core/pooling/object-pool-manager.js.map +1 -1
- package/dist/core/routing/app-integration.d.ts +3 -3
- package/dist/core/routing/app-integration.js +1 -1
- package/dist/core/routing/app-integration.js.map +1 -1
- package/dist/core/routing/index.d.ts +1 -1
- package/dist/core/routing/index.js +1 -1
- package/dist/core/routing/index.js.map +1 -1
- package/dist/core/routing/path-matcher.d.ts +6 -0
- package/dist/core/routing/path-matcher.js +46 -7
- package/dist/core/routing/path-matcher.js.map +1 -1
- package/dist/core/routing/unified-router.d.ts +4 -0
- package/dist/core/routing/unified-router.js +104 -43
- package/dist/core/routing/unified-router.js.map +1 -1
- package/dist/core/runtime/base-adapter.js +3 -3
- package/dist/core/runtime/base-adapter.js.map +1 -1
- package/dist/core/runtime/cloudflare-workers-adapter.js +1 -1
- package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -1
- package/dist/core/runtime/node-adapter.d.ts +1 -1
- package/dist/core/runtime/node-adapter.js +7 -4
- package/dist/core/runtime/node-adapter.js.map +1 -1
- package/dist/core/runtime/vercel-edge-adapter.js +1 -0
- package/dist/core/runtime/vercel-edge-adapter.js.map +1 -1
- package/dist/core/utilities/circuit-breaker.d.ts +9 -2
- package/dist/core/utilities/circuit-breaker.js +32 -3
- package/dist/core/utilities/circuit-breaker.js.map +1 -1
- package/dist/core/utilities/container.js +6 -0
- package/dist/core/utilities/container.js.map +1 -1
- package/dist/core/utilities/hooks.d.ts +4 -0
- package/dist/core/utilities/hooks.js +134 -22
- package/dist/core/utilities/hooks.js.map +1 -1
- package/dist/core/validation/index.js +6 -1
- package/dist/core/validation/index.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/moro.d.ts +154 -1
- package/dist/moro.js +592 -16
- package/dist/moro.js.map +1 -1
- package/dist/types/config.d.ts +28 -0
- package/dist/types/core.d.ts +1 -0
- package/dist/types/events.d.ts +1 -1
- package/dist/types/events.js +1 -0
- package/dist/types/events.js.map +1 -1
- package/dist/types/logger.d.ts +1 -0
- package/dist/types/module.d.ts +2 -2
- package/package.json +21 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { Logger } from '../../types/logger.js';
|
|
3
|
+
export interface LeaderElectionOptions {
|
|
4
|
+
strategy: 'file' | 'redis' | 'none';
|
|
5
|
+
lockPath?: string;
|
|
6
|
+
lockTimeout?: number;
|
|
7
|
+
heartbeatInterval?: number;
|
|
8
|
+
redisClient?: any;
|
|
9
|
+
forceLeader?: boolean;
|
|
10
|
+
instanceId?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface LeaderInfo {
|
|
13
|
+
instanceId: string;
|
|
14
|
+
hostname: string;
|
|
15
|
+
pid: number;
|
|
16
|
+
electedAt: Date;
|
|
17
|
+
lastHeartbeat: Date;
|
|
18
|
+
metadata?: Record<string, any>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* LeaderElection - Ensures only one job scheduler runs across distributed instances
|
|
22
|
+
* Supports:
|
|
23
|
+
* - File-based locking (for local/NFS environments)
|
|
24
|
+
* - Redis-based locking (for distributed systems)
|
|
25
|
+
* - Kubernetes pod detection
|
|
26
|
+
* - Automatic failover with health checks
|
|
27
|
+
*/
|
|
28
|
+
export declare class LeaderElection extends EventEmitter {
|
|
29
|
+
private isLeader;
|
|
30
|
+
private strategy;
|
|
31
|
+
private lockPath?;
|
|
32
|
+
private lockTimeout;
|
|
33
|
+
private heartbeatInterval;
|
|
34
|
+
private heartbeatTimer?;
|
|
35
|
+
private checkTimer?;
|
|
36
|
+
private logger;
|
|
37
|
+
private loggerContext;
|
|
38
|
+
private instanceId;
|
|
39
|
+
private leaderInfo?;
|
|
40
|
+
private redisClient?;
|
|
41
|
+
private isShuttingDown;
|
|
42
|
+
private lockAcquireAttempts;
|
|
43
|
+
constructor(logger: Logger, options?: LeaderElectionOptions);
|
|
44
|
+
/**
|
|
45
|
+
* Start leader election process
|
|
46
|
+
*/
|
|
47
|
+
start(): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Check if this process should participate in leader election
|
|
50
|
+
*/
|
|
51
|
+
private shouldParticipate;
|
|
52
|
+
/**
|
|
53
|
+
* Try to acquire leadership
|
|
54
|
+
*/
|
|
55
|
+
private tryAcquireLeadership;
|
|
56
|
+
/**
|
|
57
|
+
* Try to acquire file-based lock
|
|
58
|
+
*/
|
|
59
|
+
private tryAcquireFileLock;
|
|
60
|
+
/**
|
|
61
|
+
* Try to acquire Redis-based lock
|
|
62
|
+
*/
|
|
63
|
+
private tryAcquireRedisLock;
|
|
64
|
+
/**
|
|
65
|
+
* Become leader
|
|
66
|
+
*/
|
|
67
|
+
private becomeLeader;
|
|
68
|
+
/**
|
|
69
|
+
* Step down as leader
|
|
70
|
+
*/
|
|
71
|
+
private stepDown;
|
|
72
|
+
/**
|
|
73
|
+
* Start heartbeat to maintain leadership
|
|
74
|
+
*/
|
|
75
|
+
private startHeartbeat;
|
|
76
|
+
/**
|
|
77
|
+
* Stop heartbeat
|
|
78
|
+
*/
|
|
79
|
+
private stopHeartbeat;
|
|
80
|
+
/**
|
|
81
|
+
* Send heartbeat to maintain lock
|
|
82
|
+
*/
|
|
83
|
+
private sendHeartbeat;
|
|
84
|
+
/**
|
|
85
|
+
* Start periodic check for leadership changes
|
|
86
|
+
*/
|
|
87
|
+
private startPeriodicCheck;
|
|
88
|
+
/**
|
|
89
|
+
* Stop periodic check
|
|
90
|
+
*/
|
|
91
|
+
private stopPeriodicCheck;
|
|
92
|
+
/**
|
|
93
|
+
* Verify we still hold leadership
|
|
94
|
+
*/
|
|
95
|
+
private verifyLeadership;
|
|
96
|
+
/**
|
|
97
|
+
* Check health of current leader
|
|
98
|
+
*/
|
|
99
|
+
private checkLeaderHealth;
|
|
100
|
+
/**
|
|
101
|
+
* Get instance metadata
|
|
102
|
+
*/
|
|
103
|
+
private getInstanceMetadata;
|
|
104
|
+
/**
|
|
105
|
+
* Check if current instance is leader
|
|
106
|
+
*/
|
|
107
|
+
isCurrentLeader(): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Get current leader info
|
|
110
|
+
*/
|
|
111
|
+
getLeaderInfo(): LeaderInfo | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* Get instance ID
|
|
114
|
+
*/
|
|
115
|
+
getInstanceId(): string;
|
|
116
|
+
/**
|
|
117
|
+
* Force step down (for testing/admin)
|
|
118
|
+
*/
|
|
119
|
+
forceStepDown(): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* Shutdown and cleanup
|
|
122
|
+
*/
|
|
123
|
+
shutdown(): Promise<void>;
|
|
124
|
+
}
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
// Production-grade Leader Election for Distributed Job Scheduling
|
|
2
|
+
// Supports file-based and Redis-based locking for K8s and clustered environments
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import cluster from 'cluster';
|
|
8
|
+
import { isMainThread } from 'worker_threads';
|
|
9
|
+
/**
|
|
10
|
+
* LeaderElection - Ensures only one job scheduler runs across distributed instances
|
|
11
|
+
* Supports:
|
|
12
|
+
* - File-based locking (for local/NFS environments)
|
|
13
|
+
* - Redis-based locking (for distributed systems)
|
|
14
|
+
* - Kubernetes pod detection
|
|
15
|
+
* - Automatic failover with health checks
|
|
16
|
+
*/
|
|
17
|
+
export class LeaderElection extends EventEmitter {
|
|
18
|
+
isLeader = false;
|
|
19
|
+
strategy;
|
|
20
|
+
lockPath;
|
|
21
|
+
lockTimeout;
|
|
22
|
+
heartbeatInterval;
|
|
23
|
+
heartbeatTimer;
|
|
24
|
+
checkTimer;
|
|
25
|
+
logger;
|
|
26
|
+
loggerContext = 'LeaderElection';
|
|
27
|
+
instanceId;
|
|
28
|
+
leaderInfo;
|
|
29
|
+
redisClient;
|
|
30
|
+
isShuttingDown = false;
|
|
31
|
+
lockAcquireAttempts = 0;
|
|
32
|
+
constructor(logger, options = { strategy: 'file' }) {
|
|
33
|
+
super();
|
|
34
|
+
this.logger = logger;
|
|
35
|
+
this.strategy = options.strategy;
|
|
36
|
+
this.lockTimeout = options.lockTimeout ?? 30000; // 30s default
|
|
37
|
+
this.heartbeatInterval = options.heartbeatInterval ?? 10000; // 10s default
|
|
38
|
+
this.redisClient = options.redisClient;
|
|
39
|
+
// Generate unique instance ID
|
|
40
|
+
this.instanceId =
|
|
41
|
+
options.instanceId ||
|
|
42
|
+
`${os.hostname()}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
43
|
+
// Setup lock path for file-based strategy
|
|
44
|
+
if (this.strategy === 'file') {
|
|
45
|
+
if (options.lockPath) {
|
|
46
|
+
this.lockPath = options.lockPath;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const tmpDir = os.tmpdir();
|
|
50
|
+
this.lockPath = path.join(tmpDir, 'moro-jobs-leader.lock');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Force leader mode (for dev/testing)
|
|
54
|
+
if (options.forceLeader) {
|
|
55
|
+
this.isLeader = true;
|
|
56
|
+
this.logger.warn('Leader election forced - running as leader');
|
|
57
|
+
}
|
|
58
|
+
this.logger.debug('LeaderElection initialized', this.loggerContext, {
|
|
59
|
+
strategy: this.strategy,
|
|
60
|
+
instanceId: this.instanceId,
|
|
61
|
+
lockPath: this.lockPath,
|
|
62
|
+
forceLeader: options.forceLeader,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Start leader election process
|
|
67
|
+
*/
|
|
68
|
+
async start() {
|
|
69
|
+
if (this.strategy === 'none') {
|
|
70
|
+
this.logger.debug('Leader election disabled, assuming leader role');
|
|
71
|
+
this.becomeLeader();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Check if we should participate in leader election
|
|
75
|
+
if (!this.shouldParticipate()) {
|
|
76
|
+
this.logger.info('Not participating in leader election (worker process/thread)');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.logger.info('Starting leader election...', this.loggerContext, {
|
|
80
|
+
strategy: this.strategy,
|
|
81
|
+
instanceId: this.instanceId,
|
|
82
|
+
});
|
|
83
|
+
// Try to acquire leadership
|
|
84
|
+
await this.tryAcquireLeadership();
|
|
85
|
+
// Start periodic checks
|
|
86
|
+
this.startPeriodicCheck();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Check if this process should participate in leader election
|
|
90
|
+
*/
|
|
91
|
+
shouldParticipate() {
|
|
92
|
+
// Only main thread and primary cluster process can be leader
|
|
93
|
+
if (!isMainThread) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
if (cluster.isWorker) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Try to acquire leadership
|
|
103
|
+
*/
|
|
104
|
+
async tryAcquireLeadership() {
|
|
105
|
+
if (this.isShuttingDown) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
this.lockAcquireAttempts++;
|
|
109
|
+
try {
|
|
110
|
+
let acquired = false;
|
|
111
|
+
if (this.strategy === 'file') {
|
|
112
|
+
acquired = await this.tryAcquireFileLock();
|
|
113
|
+
}
|
|
114
|
+
else if (this.strategy === 'redis') {
|
|
115
|
+
acquired = await this.tryAcquireRedisLock();
|
|
116
|
+
}
|
|
117
|
+
if (acquired) {
|
|
118
|
+
this.becomeLeader();
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Check if current leader is still alive
|
|
123
|
+
await this.checkLeaderHealth();
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
this.logger.error('Error during leader election', this.loggerContext, {
|
|
129
|
+
error,
|
|
130
|
+
attempt: this.lockAcquireAttempts,
|
|
131
|
+
});
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Try to acquire file-based lock
|
|
137
|
+
*/
|
|
138
|
+
async tryAcquireFileLock() {
|
|
139
|
+
if (!this.lockPath) {
|
|
140
|
+
throw new Error('Lock path not configured');
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
// Check if lock file exists
|
|
144
|
+
const exists = await fs.promises
|
|
145
|
+
.access(this.lockPath)
|
|
146
|
+
.then(() => true)
|
|
147
|
+
.catch(() => false);
|
|
148
|
+
if (exists) {
|
|
149
|
+
// Read existing lock
|
|
150
|
+
const lockData = await fs.promises.readFile(this.lockPath, 'utf-8');
|
|
151
|
+
const existingLeader = JSON.parse(lockData);
|
|
152
|
+
// Check if lock is expired
|
|
153
|
+
const lockAge = Date.now() - new Date(existingLeader.lastHeartbeat).getTime();
|
|
154
|
+
if (lockAge < this.lockTimeout) {
|
|
155
|
+
// Lock is still valid
|
|
156
|
+
this.leaderInfo = existingLeader;
|
|
157
|
+
this.logger.debug('Leader lock held by another instance', this.loggerContext, {
|
|
158
|
+
leader: existingLeader.instanceId,
|
|
159
|
+
age: lockAge,
|
|
160
|
+
});
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
// Lock expired, take over
|
|
164
|
+
this.logger.warn('Leader lock expired, taking over', this.loggerContext, {
|
|
165
|
+
previousLeader: existingLeader.instanceId,
|
|
166
|
+
lockAge,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// Create/update lock file
|
|
170
|
+
const lockInfo = {
|
|
171
|
+
instanceId: this.instanceId,
|
|
172
|
+
hostname: os.hostname(),
|
|
173
|
+
pid: process.pid,
|
|
174
|
+
electedAt: new Date(),
|
|
175
|
+
lastHeartbeat: new Date(),
|
|
176
|
+
metadata: this.getInstanceMetadata(),
|
|
177
|
+
};
|
|
178
|
+
await fs.promises.writeFile(this.lockPath, JSON.stringify(lockInfo, null, 2), 'utf-8');
|
|
179
|
+
this.leaderInfo = lockInfo;
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
this.logger.error('Failed to acquire file lock', this.loggerContext, {
|
|
184
|
+
error,
|
|
185
|
+
lockPath: this.lockPath,
|
|
186
|
+
});
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Try to acquire Redis-based lock
|
|
192
|
+
*/
|
|
193
|
+
async tryAcquireRedisLock() {
|
|
194
|
+
if (!this.redisClient) {
|
|
195
|
+
throw new Error('Redis client not configured');
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
const lockKey = 'moro:jobs:leader:lock';
|
|
199
|
+
const lockInfo = {
|
|
200
|
+
instanceId: this.instanceId,
|
|
201
|
+
hostname: os.hostname(),
|
|
202
|
+
pid: process.pid,
|
|
203
|
+
electedAt: new Date(),
|
|
204
|
+
lastHeartbeat: new Date(),
|
|
205
|
+
metadata: this.getInstanceMetadata(),
|
|
206
|
+
};
|
|
207
|
+
// Try to set lock with NX (only if not exists) and PX (expiration in ms)
|
|
208
|
+
const result = await this.redisClient.set(lockKey, JSON.stringify(lockInfo), 'PX', this.lockTimeout, 'NX');
|
|
209
|
+
if (result === 'OK') {
|
|
210
|
+
this.leaderInfo = lockInfo;
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
// Lock exists, get current leader info
|
|
214
|
+
const existingLock = await this.redisClient.get(lockKey);
|
|
215
|
+
if (existingLock) {
|
|
216
|
+
this.leaderInfo = JSON.parse(existingLock);
|
|
217
|
+
}
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
this.logger.error('Failed to acquire Redis lock', this.loggerContext, { error });
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Become leader
|
|
227
|
+
*/
|
|
228
|
+
becomeLeader() {
|
|
229
|
+
if (this.isLeader) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
this.isLeader = true;
|
|
233
|
+
this.logger.info('Became leader', this.loggerContext, {
|
|
234
|
+
instanceId: this.instanceId,
|
|
235
|
+
strategy: this.strategy,
|
|
236
|
+
attempts: this.lockAcquireAttempts,
|
|
237
|
+
});
|
|
238
|
+
this.emit('leader:elected', {
|
|
239
|
+
instanceId: this.instanceId,
|
|
240
|
+
electedAt: this.leaderInfo?.electedAt,
|
|
241
|
+
});
|
|
242
|
+
// Start heartbeat
|
|
243
|
+
this.startHeartbeat();
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Step down as leader
|
|
247
|
+
*/
|
|
248
|
+
async stepDown() {
|
|
249
|
+
if (!this.isLeader) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
this.logger.info('Stepping down as leader', this.loggerContext, {
|
|
253
|
+
instanceId: this.instanceId,
|
|
254
|
+
});
|
|
255
|
+
this.isLeader = false;
|
|
256
|
+
this.stopHeartbeat();
|
|
257
|
+
// Release lock
|
|
258
|
+
try {
|
|
259
|
+
if (this.strategy === 'file' && this.lockPath) {
|
|
260
|
+
await fs.promises.unlink(this.lockPath).catch(() => { });
|
|
261
|
+
}
|
|
262
|
+
else if (this.strategy === 'redis' && this.redisClient) {
|
|
263
|
+
await this.redisClient.del('moro:jobs:leader:lock');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
this.logger.error('Failed to release lock during step down', this.loggerContext, { error });
|
|
268
|
+
}
|
|
269
|
+
this.emit('leader:stepdown', { instanceId: this.instanceId });
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Start heartbeat to maintain leadership
|
|
273
|
+
*/
|
|
274
|
+
startHeartbeat() {
|
|
275
|
+
if (this.heartbeatTimer) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
this.heartbeatTimer = setInterval(async () => {
|
|
279
|
+
if (this.isShuttingDown) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
await this.sendHeartbeat();
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
this.logger.error('Heartbeat failed', this.loggerContext, { error });
|
|
287
|
+
// Lost leadership
|
|
288
|
+
await this.stepDown();
|
|
289
|
+
// Try to reacquire
|
|
290
|
+
setTimeout(() => this.tryAcquireLeadership(), 1000);
|
|
291
|
+
}
|
|
292
|
+
}, this.heartbeatInterval);
|
|
293
|
+
// Don't keep process alive
|
|
294
|
+
this.heartbeatTimer.unref();
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Stop heartbeat
|
|
298
|
+
*/
|
|
299
|
+
stopHeartbeat() {
|
|
300
|
+
if (this.heartbeatTimer) {
|
|
301
|
+
clearInterval(this.heartbeatTimer);
|
|
302
|
+
this.heartbeatTimer = undefined;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Send heartbeat to maintain lock
|
|
307
|
+
*/
|
|
308
|
+
async sendHeartbeat() {
|
|
309
|
+
if (!this.leaderInfo) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
this.leaderInfo.lastHeartbeat = new Date();
|
|
313
|
+
if (this.strategy === 'file' && this.lockPath) {
|
|
314
|
+
await fs.promises.writeFile(this.lockPath, JSON.stringify(this.leaderInfo, null, 2), 'utf-8');
|
|
315
|
+
}
|
|
316
|
+
else if (this.strategy === 'redis' && this.redisClient) {
|
|
317
|
+
const lockKey = 'moro:jobs:leader:lock';
|
|
318
|
+
await this.redisClient.set(lockKey, JSON.stringify(this.leaderInfo), 'PX', this.lockTimeout);
|
|
319
|
+
}
|
|
320
|
+
this.logger.debug('Heartbeat sent', this.loggerContext, { instanceId: this.instanceId });
|
|
321
|
+
this.emit('leader:heartbeat', {
|
|
322
|
+
instanceId: this.instanceId,
|
|
323
|
+
timestamp: this.leaderInfo.lastHeartbeat,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Start periodic check for leadership changes
|
|
328
|
+
*/
|
|
329
|
+
startPeriodicCheck() {
|
|
330
|
+
if (this.checkTimer) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const checkInterval = Math.floor(this.heartbeatInterval * 1.5);
|
|
334
|
+
this.checkTimer = setInterval(async () => {
|
|
335
|
+
if (this.isShuttingDown) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (!this.isLeader) {
|
|
339
|
+
// Try to acquire leadership
|
|
340
|
+
await this.tryAcquireLeadership();
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
// Verify we still hold the lock
|
|
344
|
+
await this.verifyLeadership();
|
|
345
|
+
}
|
|
346
|
+
}, checkInterval);
|
|
347
|
+
// Don't keep process alive
|
|
348
|
+
this.checkTimer.unref();
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Stop periodic check
|
|
352
|
+
*/
|
|
353
|
+
stopPeriodicCheck() {
|
|
354
|
+
if (this.checkTimer) {
|
|
355
|
+
clearInterval(this.checkTimer);
|
|
356
|
+
this.checkTimer = undefined;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Verify we still hold leadership
|
|
361
|
+
*/
|
|
362
|
+
async verifyLeadership() {
|
|
363
|
+
if (!this.isLeader) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
let stillLeader = false;
|
|
368
|
+
if (this.strategy === 'file' && this.lockPath) {
|
|
369
|
+
const lockData = await fs.promises.readFile(this.lockPath, 'utf-8');
|
|
370
|
+
const currentLeader = JSON.parse(lockData);
|
|
371
|
+
stillLeader = currentLeader.instanceId === this.instanceId;
|
|
372
|
+
}
|
|
373
|
+
else if (this.strategy === 'redis' && this.redisClient) {
|
|
374
|
+
const lockData = await this.redisClient.get('moro:jobs:leader:lock');
|
|
375
|
+
if (lockData) {
|
|
376
|
+
const currentLeader = JSON.parse(lockData);
|
|
377
|
+
stillLeader = currentLeader.instanceId === this.instanceId;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (!stillLeader) {
|
|
381
|
+
this.logger.warn('Lost leadership', this.loggerContext, { instanceId: this.instanceId });
|
|
382
|
+
await this.stepDown();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
this.logger.error('Failed to verify leadership', this.loggerContext, { error });
|
|
387
|
+
await this.stepDown();
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Check health of current leader
|
|
392
|
+
*/
|
|
393
|
+
async checkLeaderHealth() {
|
|
394
|
+
if (!this.leaderInfo || this.leaderInfo.instanceId === this.instanceId) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const heartbeatAge = Date.now() - new Date(this.leaderInfo.lastHeartbeat).getTime();
|
|
398
|
+
if (heartbeatAge > this.lockTimeout) {
|
|
399
|
+
this.logger.warn('Current leader appears unhealthy', this.loggerContext, {
|
|
400
|
+
leader: this.leaderInfo.instanceId,
|
|
401
|
+
heartbeatAge,
|
|
402
|
+
});
|
|
403
|
+
this.emit('leader:unhealthy', {
|
|
404
|
+
leader: this.leaderInfo.instanceId,
|
|
405
|
+
heartbeatAge,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Get instance metadata
|
|
411
|
+
*/
|
|
412
|
+
getInstanceMetadata() {
|
|
413
|
+
const metadata = {
|
|
414
|
+
nodeVersion: process.version,
|
|
415
|
+
platform: process.platform,
|
|
416
|
+
arch: process.arch,
|
|
417
|
+
uptime: process.uptime(),
|
|
418
|
+
};
|
|
419
|
+
// K8s detection
|
|
420
|
+
if (process.env.KUBERNETES_SERVICE_HOST) {
|
|
421
|
+
metadata.kubernetes = {
|
|
422
|
+
podName: process.env.HOSTNAME || os.hostname(),
|
|
423
|
+
namespace: process.env.POD_NAMESPACE,
|
|
424
|
+
nodeName: process.env.NODE_NAME,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
// Container detection
|
|
428
|
+
if (process.env.CONTAINER) {
|
|
429
|
+
metadata.container = process.env.CONTAINER;
|
|
430
|
+
}
|
|
431
|
+
return metadata;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Check if current instance is leader
|
|
435
|
+
*/
|
|
436
|
+
isCurrentLeader() {
|
|
437
|
+
return this.isLeader;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Get current leader info
|
|
441
|
+
*/
|
|
442
|
+
getLeaderInfo() {
|
|
443
|
+
return this.leaderInfo ? { ...this.leaderInfo } : undefined;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Get instance ID
|
|
447
|
+
*/
|
|
448
|
+
getInstanceId() {
|
|
449
|
+
return this.instanceId;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Force step down (for testing/admin)
|
|
453
|
+
*/
|
|
454
|
+
async forceStepDown() {
|
|
455
|
+
if (!this.isLeader) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
this.logger.warn('Forced step down requested', this.loggerContext, {
|
|
459
|
+
instanceId: this.instanceId,
|
|
460
|
+
});
|
|
461
|
+
await this.stepDown();
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Shutdown and cleanup
|
|
465
|
+
*/
|
|
466
|
+
async shutdown() {
|
|
467
|
+
this.logger.info('LeaderElection shutting down...', this.loggerContext, {
|
|
468
|
+
instanceId: this.instanceId,
|
|
469
|
+
wasLeader: this.isLeader,
|
|
470
|
+
});
|
|
471
|
+
this.isShuttingDown = true;
|
|
472
|
+
this.stopHeartbeat();
|
|
473
|
+
this.stopPeriodicCheck();
|
|
474
|
+
if (this.isLeader) {
|
|
475
|
+
await this.stepDown();
|
|
476
|
+
}
|
|
477
|
+
this.removeAllListeners();
|
|
478
|
+
this.logger.info('LeaderElection shutdown complete');
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
//# sourceMappingURL=leader-election.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader-election.js","sourceRoot":"","sources":["../../../src/core/jobs/leader-election.ts"],"names":[],"mappings":"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"}
|