1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | var vizion = require('vizion');
|
7 | var cst = require('../constants.js');
|
8 | var eachLimit = require('async/eachLimit');
|
9 | var debug = require('debug')('pm2:worker');
|
10 | var domain = require('domain');
|
11 | var cronJob = require('cron').CronJob
|
12 |
|
13 | module.exports = function(God) {
|
14 | var timer = null;
|
15 |
|
16 | God.CronJobs = new Map();
|
17 | God.Worker = {};
|
18 | God.Worker.is_running = false;
|
19 |
|
20 | var _getProcessById = function(pm_id) {
|
21 | var proc = God.clusters_db[pm_id];
|
22 | return proc ? proc : null;
|
23 | };
|
24 |
|
25 | var registerCron = function(proc_key) {
|
26 | var proc = _getProcessById(proc_key.pm2_env.pm_id);
|
27 |
|
28 | if (!proc ||
|
29 | !proc.pm2_env ||
|
30 | proc.pm2_env.pm_id === undefined ||
|
31 | !proc.pm2_env.cron_restart ||
|
32 | God.CronJobs.has(proc.pm2_env.pm_id))
|
33 | return;
|
34 |
|
35 | console.log('[PM2][WORKER] Registering a cron job on:', proc.pm2_env.pm_id);
|
36 |
|
37 | var job = new cronJob({
|
38 | cronTime: proc.pm2_env.cron_restart,
|
39 | onTick: function() {
|
40 | God.softReloadProcessId({id: proc.pm2_env.pm_id}, function(err, data) {
|
41 | if (err)
|
42 | console.error(err.stack || err);
|
43 | return;
|
44 | });
|
45 | },
|
46 | start: false
|
47 | });
|
48 |
|
49 | job.start();
|
50 | God.CronJobs.set(proc.pm2_env.pm_id, job);
|
51 | };
|
52 |
|
53 |
|
54 | var maxMemoryRestart = function(proc_key, cb) {
|
55 | var proc = _getProcessById(proc_key.pm2_env.pm_id);
|
56 |
|
57 | if (!(proc &&
|
58 | proc.pm2_env &&
|
59 | proc_key.monit))
|
60 | return cb();
|
61 |
|
62 | if (proc_key.monit.memory !== undefined &&
|
63 | proc.pm2_env.max_memory_restart !== undefined &&
|
64 | proc.pm2_env.max_memory_restart < proc_key.monit.memory &&
|
65 | proc.pm2_env.axm_options &&
|
66 | proc.pm2_env.axm_options.pid === undefined) {
|
67 | console.log('[PM2][WORKER] Process %s restarted because it exceeds --max-memory-restart value (current_memory=%s max_memory_limit=%s [octets])', proc.pm2_env.pm_id, proc_key.monit.memory, proc.pm2_env.max_memory_restart);
|
68 | God.softReloadProcessId({
|
69 | id : proc.pm2_env.pm_id
|
70 | }, function(err, data) {
|
71 | if (err)
|
72 | console.error(err.stack || err);
|
73 | return cb();
|
74 | });
|
75 | }
|
76 | else {
|
77 | return cb();
|
78 | }
|
79 | };
|
80 |
|
81 |
|
82 | var versioningRefresh = function(proc_key, cb) {
|
83 | var proc = _getProcessById(proc_key.pm2_env.pm_id);
|
84 | if (!(proc &&
|
85 | proc.pm2_env &&
|
86 | (proc.pm2_env.vizion !== false && proc.pm2_env.vizion != "false") &&
|
87 | proc.pm2_env.versioning &&
|
88 | proc.pm2_env.versioning.repo_path)) {
|
89 | return cb();
|
90 | }
|
91 |
|
92 | if (proc.pm2_env.vizion_running === true)
|
93 | {
|
94 | debug('Vizion is already running for proc id: %d, skipping this round', proc.pm2_env.pm_id);
|
95 | return cb();
|
96 | }
|
97 |
|
98 | proc.pm2_env.vizion_running = true;
|
99 | var repo_path = proc.pm2_env.versioning.repo_path;
|
100 |
|
101 | vizion.analyze({
|
102 | folder: proc.pm2_env.versioning.repo_path
|
103 | },
|
104 | function(err, meta) {
|
105 | if (err != null)
|
106 | return cb();
|
107 |
|
108 | proc = _getProcessById(proc_key.pm2_env.pm_id);
|
109 |
|
110 | if (!(proc &&
|
111 | proc.pm2_env &&
|
112 | proc.pm2_env.versioning &&
|
113 | proc.pm2_env.versioning.repo_path)) {
|
114 | console.error('Proc not defined anymore or versioning unknown');
|
115 | return cb();
|
116 | }
|
117 |
|
118 | proc.pm2_env.vizion_running = false;
|
119 | meta.repo_path = repo_path;
|
120 | proc.pm2_env.versioning = meta;
|
121 | debug('[PM2][WORKER] %s parsed for versioning', proc.pm2_env.name);
|
122 | return cb();
|
123 | });
|
124 | };
|
125 |
|
126 | var tasks = function() {
|
127 | if (God.Worker.is_running === true) {
|
128 | debug('[PM2][WORKER] Worker is already running, skipping this round');
|
129 | return false;
|
130 | }
|
131 | God.Worker.is_running = true;
|
132 |
|
133 | God.getMonitorData(null, function(err, data) {
|
134 | if (err || !data || typeof(data) !== 'object') {
|
135 | God.Worker.is_running = false;
|
136 | return console.error(err);
|
137 | }
|
138 |
|
139 | eachLimit(data, 1, function(proc, next) {
|
140 | if (!proc || !proc.pm2_env || proc.pm2_env.pm_id === undefined)
|
141 | return next();
|
142 |
|
143 | debug('[PM2][WORKER] Processing proc id:', proc.pm2_env.pm_id);
|
144 |
|
145 |
|
146 | if (proc.pm2_env.exp_backoff_restart_delay !== undefined &&
|
147 | proc.pm2_env.prev_restart_delay && proc.pm2_env.prev_restart_delay > 0) {
|
148 | var app_uptime = Date.now() - proc.pm2_env.pm_uptime
|
149 | if (app_uptime > cst.EXP_BACKOFF_RESET_TIMER) {
|
150 | var ref_proc = _getProcessById(proc.pm2_env.pm_id);
|
151 | ref_proc.pm2_env.prev_restart_delay = 0
|
152 | console.log(`[PM2][WORKER] Reset the restart delay, as app ${proc.name} is up for more than ${cst.EXP_BACKOFF_RESET_TIMER}`)
|
153 | }
|
154 | }
|
155 |
|
156 | registerCron(proc);
|
157 |
|
158 | maxMemoryRestart(proc, function() {
|
159 | return next();
|
160 | });
|
161 | }, function(err) {
|
162 | God.Worker.is_running = false;
|
163 | debug('[PM2][WORKER] My job here is done, next job in %d seconds', parseInt(cst.WORKER_INTERVAL / 1000));
|
164 | });
|
165 | });
|
166 | };
|
167 |
|
168 | var wrappedTasks = function() {
|
169 | var d = domain.create();
|
170 |
|
171 | d.once('error', function(err) {
|
172 | console.error('[PM2][WORKER] Error caught by domain:\n' + (err.stack || err));
|
173 | God.Worker.is_running = false;
|
174 | });
|
175 |
|
176 | d.run(function() {
|
177 | tasks();
|
178 | });
|
179 | };
|
180 |
|
181 |
|
182 | God.Worker.start = function() {
|
183 | timer = setInterval(wrappedTasks, cst.WORKER_INTERVAL);
|
184 | };
|
185 |
|
186 | God.Worker.stop = function() {
|
187 | if (timer !== null)
|
188 | clearInterval(timer);
|
189 | };
|
190 | };
|