UNPKG

6.26 kBJavaScriptView Raw
1/**
2 * Copyright 2013-2021 the PM2 project authors. All rights reserved.
3 * Use of this source code is governed by a license that
4 * can be found in the LICENSE file.
5 */
6var vizion = require('vizion');
7var cst = require('../constants.js');
8var eachLimit = require('async/eachLimit');
9var debug = require('debug')('pm2:worker');
10var domain = require('domain');
11var cronJob = require('cron').CronJob
12var vCheck = require('./VersionCheck.js')
13var pkg = require('../package.json')
14
15module.exports = function(God) {
16 var timer = null;
17
18 God.CronJobs = new Map();
19 God.Worker = {};
20 God.Worker.is_running = false;
21
22 God.getCronID = function(pm_id) {
23 return `cron-${pm_id}`
24 }
25
26 God.registerCron = function(pm2_env) {
27 if (!pm2_env ||
28 pm2_env.pm_id === undefined ||
29 !pm2_env.cron_restart ||
30 God.CronJobs.has(God.getCronID(pm2_env.pm_id)))
31 return;
32
33 var pm_id = pm2_env.pm_id
34 console.log('[PM2][WORKER] Registering a cron job on:', pm_id);
35
36 var job = new cronJob({
37 cronTime: pm2_env.cron_restart,
38 onTick: function() {
39 God.restartProcessId({id: pm_id}, function(err, data) {
40 if (err)
41 console.error(err.stack || err);
42 return;
43 });
44 },
45 start: false
46 });
47
48 job.start();
49 God.CronJobs.set(God.getCronID(pm_id), job);
50 }
51
52
53 /**
54 * Deletes the cron job on deletion of process
55 */
56 God.deleteCron = function(id) {
57 if (typeof(id) !== 'undefined' && God.CronJobs.has(God.getCronID(id)) === false)
58 return;
59 console.log('[PM2] Deregistering a cron job on:', id);
60 var job = God.CronJobs.get(God.getCronID(id));
61 job.stop();
62 God.CronJobs.delete(God.getCronID(id));
63 };
64
65 var _getProcessById = function(pm_id) {
66 var proc = God.clusters_db[pm_id];
67 return proc ? proc : null;
68 };
69
70
71 var maxMemoryRestart = function(proc_key, cb) {
72 var proc = _getProcessById(proc_key.pm2_env.pm_id);
73
74 if (!(proc &&
75 proc.pm2_env &&
76 proc_key.monit))
77 return cb();
78
79 if (proc_key.monit.memory !== undefined &&
80 proc.pm2_env.max_memory_restart !== undefined &&
81 proc.pm2_env.max_memory_restart < proc_key.monit.memory &&
82 proc.pm2_env.axm_options &&
83 proc.pm2_env.axm_options.pid === undefined) {
84 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);
85 God.reloadProcessId({
86 id : proc.pm2_env.pm_id
87 }, function(err, data) {
88 if (err)
89 console.error(err.stack || err);
90 return cb();
91 });
92 }
93 else {
94 return cb();
95 }
96 };
97
98 // Deprecated
99 var versioningRefresh = function(proc_key, cb) {
100 var proc = _getProcessById(proc_key.pm2_env.pm_id);
101 if (!(proc &&
102 proc.pm2_env &&
103 (proc.pm2_env.vizion !== false && proc.pm2_env.vizion != "false") &&
104 proc.pm2_env.versioning &&
105 proc.pm2_env.versioning.repo_path)) {
106 return cb();
107 }
108
109 if (proc.pm2_env.vizion_running === true)
110 {
111 debug('Vizion is already running for proc id: %d, skipping this round', proc.pm2_env.pm_id);
112 return cb();
113 }
114
115 proc.pm2_env.vizion_running = true;
116 var repo_path = proc.pm2_env.versioning.repo_path;
117
118 vizion.analyze({
119 folder: proc.pm2_env.versioning.repo_path
120 },
121 function(err, meta) {
122 if (err != null)
123 return cb();
124
125 proc = _getProcessById(proc_key.pm2_env.pm_id);
126
127 if (!(proc &&
128 proc.pm2_env &&
129 proc.pm2_env.versioning &&
130 proc.pm2_env.versioning.repo_path)) {
131 console.error('Proc not defined anymore or versioning unknown');
132 return cb();
133 }
134
135 proc.pm2_env.vizion_running = false;
136 meta.repo_path = repo_path;
137 proc.pm2_env.versioning = meta;
138 debug('[PM2][WORKER] %s parsed for versioning', proc.pm2_env.name);
139 return cb();
140 });
141 };
142
143 var tasks = function() {
144 if (God.Worker.is_running === true) {
145 debug('[PM2][WORKER] Worker is already running, skipping this round');
146 return false;
147 }
148 God.Worker.is_running = true;
149
150 God.getMonitorData(null, function(err, data) {
151 if (err || !data || typeof(data) !== 'object') {
152 God.Worker.is_running = false;
153 return console.error(err);
154 }
155
156 eachLimit(data, 1, function(proc, next) {
157 if (!proc || !proc.pm2_env || proc.pm2_env.pm_id === undefined)
158 return next();
159
160 debug('[PM2][WORKER] Processing proc id:', proc.pm2_env.pm_id);
161
162 // Reset restart delay if application has an uptime of more > 30secs
163 if (proc.pm2_env.exp_backoff_restart_delay !== undefined &&
164 proc.pm2_env.prev_restart_delay && proc.pm2_env.prev_restart_delay > 0) {
165 var app_uptime = Date.now() - proc.pm2_env.pm_uptime
166 if (app_uptime > cst.EXP_BACKOFF_RESET_TIMER) {
167 var ref_proc = _getProcessById(proc.pm2_env.pm_id);
168 ref_proc.pm2_env.prev_restart_delay = 0
169 console.log(`[PM2][WORKER] Reset the restart delay, as app ${proc.name} has been up for more than ${cst.EXP_BACKOFF_RESET_TIMER}ms`)
170 }
171 }
172
173 // Check if application has reached memory threshold
174 maxMemoryRestart(proc, function() {
175 return next();
176 });
177 }, function(err) {
178 God.Worker.is_running = false;
179 debug('[PM2][WORKER] My job here is done, next job in %d seconds', parseInt(cst.WORKER_INTERVAL / 1000));
180 });
181 });
182 };
183
184 var wrappedTasks = function() {
185 var d = domain.create();
186
187 d.once('error', function(err) {
188 console.error('[PM2][WORKER] Error caught by domain:\n' + (err.stack || err));
189 God.Worker.is_running = false;
190 });
191
192 d.run(function() {
193 tasks();
194 });
195 };
196
197
198 God.Worker.start = function() {
199 timer = setInterval(wrappedTasks, cst.WORKER_INTERVAL);
200
201 setInterval(() => {
202 vCheck({
203 state: 'check',
204 version: pkg.version
205 })
206 }, 1000 * 60 * 60 * 24)
207 };
208
209 God.Worker.stop = function() {
210 if (timer !== null)
211 clearInterval(timer);
212 };
213};