UNPKG

6.23 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 */
6'use strict';
7
8/**
9 * @file Reload functions related
10 * @author Alexandre Strzelewicz <as@unitech.io>
11 * @project PM2
12 */
13
14var cst = require('../../constants.js');
15var Utility = require('../Utility.js');
16
17/**
18 * softReload will wait permission from process to exit
19 * @method softReload
20 * @param {} God
21 * @param {} id
22 * @param {} cb
23 * @return Literal
24 */
25function softReload(God, id, cb) {
26 var t_key = '_old_' + id;
27
28 // Move old worker to tmp id
29 God.clusters_db[t_key] = God.clusters_db[id];
30
31 delete God.clusters_db[id];
32
33 var old_worker = God.clusters_db[t_key];
34
35 // Deep copy
36 var new_env = Utility.clone(old_worker.pm2_env);
37
38 // Reset created_at and unstable_restarts
39 God.resetState(new_env);
40
41 new_env.restart_time += 1;
42
43 old_worker.pm2_env.pm_id = t_key;
44 old_worker.pm_id = t_key;
45
46 God.executeApp(new_env, function(err, new_worker) {
47 if (err) return cb(err);
48
49 var timer = null;
50
51 var onListen = function () {
52 clearTimeout(timer);
53 softCleanDeleteProcess();
54 console.log('-softReload- New worker listening');
55 };
56
57 // Bind to know when the new process is up
58 new_worker.once('listening', onListen);
59
60 timer = setTimeout(function() {
61 new_worker.removeListener('listening', onListen);
62 softCleanDeleteProcess();
63 }, new_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
64
65 // Remove old worker properly
66 var softCleanDeleteProcess = function () {
67 var cleanUp = function () {
68 clearTimeout(timer);
69 console.log('-softReload- Old worker disconnected');
70 return God.deleteProcessId(t_key, cb);
71 };
72
73 old_worker.once('disconnect', cleanUp);
74
75 try {
76 if (old_worker.state != 'dead' && old_worker.state != 'disconnected')
77 old_worker.send && old_worker.send('shutdown');
78 else {
79 clearTimeout(timer);
80 console.error('Worker %d is already disconnected', old_worker.pm2_env.pm_id);
81 return God.deleteProcessId(t_key, cb);
82 }
83 } catch(e) {
84 clearTimeout(timer);
85 console.error('Worker %d is already disconnected', old_worker.pm2_env.pm_id);
86 return God.deleteProcessId(t_key, cb);
87 }
88
89 timer = setTimeout(function () {
90 old_worker.removeListener('disconnect', cleanUp);
91 return God.deleteProcessId(t_key, cb);
92 }, cst.GRACEFUL_TIMEOUT);
93 return false;
94 };
95 return false;
96 });
97 return false;
98};
99
100/**
101 * hardReload will reload without waiting permission from process
102 * @method hardReload
103 * @param {} God
104 * @param {} id
105 * @param {} cb
106 * @return Literal
107 */
108function hardReload(God, id, wait_msg, cb) {
109 var t_key = '_old_' + id;
110
111 // Move old worker to tmp id
112 God.clusters_db[t_key] = God.clusters_db[id];
113 delete God.clusters_db[id];
114
115 var old_worker = God.clusters_db[t_key];
116 // Deep copy
117 var new_env = Utility.clone(old_worker.pm2_env);
118 new_env.restart_time += 1;
119
120 // Reset created_at and unstable_restarts
121 God.resetState(new_env);
122
123 old_worker.pm2_env.pm_id = t_key;
124 old_worker.pm_id = t_key;
125 var timer = null;
126 var readySignalSent = false;
127
128 var onListen = function () {
129 clearTimeout(timer);
130 readySignalSent = true;
131 console.log('-reload- New worker listening');
132 return God.deleteProcessId(t_key, cb);
133 };
134
135 var listener = function (packet) {
136 if (packet.raw === 'ready' &&
137 packet.process.name === old_worker.pm2_env.name &&
138 packet.process.pm_id === id) {
139 God.bus.removeListener('process:msg', listener);
140 return onListen();
141 }
142 };
143
144 if (wait_msg !== 'listening') {
145 God.bus.on('process:msg', listener);
146 }
147
148 God.executeApp(new_env, function(err, new_worker) {
149 if (err) return cb(err);
150
151 // Bind to know when the new process is up
152 if (wait_msg === 'listening') {
153 new_worker.once('listening', onListen);
154 }
155
156 timer = setTimeout(function() {
157 if (readySignalSent) {
158 return;
159 }
160
161 if (wait_msg === 'listening')
162 new_worker.removeListener(wait_msg, onListen);
163 else
164 God.bus.removeListener('process:msg', listener);
165
166 return God.deleteProcessId(t_key, cb);
167 }, new_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
168
169 return false;
170 });
171 return false;
172};
173
174/**
175 * Description
176 * @method exports
177 * @param {} God
178 * @return
179 */
180module.exports = function(God) {
181
182 /**
183 * Reload
184 * @method softReloadProcessId
185 * @param {} id
186 * @param {} cb
187 * @return CallExpression
188 */
189 God.softReloadProcessId = function(opts, cb) {
190 var id = opts.id;
191 var env = opts.env || {};
192
193 if (!(id in God.clusters_db))
194 return cb(new Error(`pm_id ${id} not available in ${id}`));
195
196 if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS &&
197 God.clusters_db[id].pm2_env.exec_mode == 'cluster_mode' &&
198 !God.clusters_db[id].pm2_env.wait_ready) {
199
200 Utility.extend(God.clusters_db[id].pm2_env.env, opts.env);
201 Utility.extendExtraConfig(God.clusters_db[id], opts);
202
203 return softReload(God, id, cb);
204 }
205 else {
206 console.log('Process %s in a stopped status, starting it', id);
207 return God.restartProcessId(opts, cb);
208 }
209 };
210
211 /**
212 * Reload
213 * @method reloadProcessId
214 * @param {} id
215 * @param {} cb
216 * @return CallExpression
217 */
218 God.reloadProcessId = function(opts, cb) {
219 var id = opts.id;
220 var env = opts.env || {};
221
222 if (!(id in God.clusters_db))
223 return cb(new Error('PM2 ID unknown'));
224
225 if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS &&
226 God.clusters_db[id].pm2_env.exec_mode == 'cluster_mode') {
227
228 Utility.extend(God.clusters_db[id].pm2_env.env, opts.env);
229 Utility.extendExtraConfig(God.clusters_db[id], opts);
230
231 var wait_msg = God.clusters_db[id].pm2_env.wait_ready ? 'ready' : 'listening';
232 return hardReload(God, id, wait_msg, cb);
233 }
234 else {
235 console.log('Process %s in a stopped status, starting it', id);
236 return God.restartProcessId(opts, cb);
237 }
238 };
239
240};